From 2703c51aed8b727654d10f48d523fe2a753fc45b Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 24 Dec 2024 05:10:14 +0000 Subject: [PATCH] Build 2024-12-24 05:10:14. 16ef7fc1. Event: schedule. --- 404.html | 2 +- 404/index.html | 2 +- .../vJuFcpWvbN6zbAub4gcIZ/_buildManifest.js | 1 + .../vJuFcpWvbN6zbAub4gcIZ/_ssgManifest.js | 1 + .../augmentations/crops/transforms/index.html | 4 +- .../domain_adaptation/transforms/index.html | 6 +- .../dropout/xy_masking/index.html | 208 +++---- .../geometric/functional/index.html | 417 ++++++++------ .../augmentations/geometric/rotate/index.html | 6 +- .../geometric/transforms/index.html | 407 +++++++------- .../transforms3d/functional/index.html | 129 +++-- .../transforms3d/transforms/index.html | 249 +++++---- docs/api_reference/core/bbox_utils/index.html | 182 ++++--- .../api_reference/core/composition/index.html | 46 +- .../core/keypoints_utils/index.html | 509 +++++++++++------- .../core/transforms_interface/index.html | 76 +-- docs/api_reference/full_reference/index.html | 2 +- docs/search/search_index.json | 2 +- docs/sitemap.xml | 182 +++---- docs/sitemap.xml.gz | Bin 967 -> 967 bytes index.html | 4 +- index.txt | 4 +- people/index.html | 2 +- people/index.txt | 2 +- sitemap.xml | 188 +++---- testimonials/index.html | 2 +- testimonials/index.txt | 2 +- 27 files changed, 1471 insertions(+), 1164 deletions(-) create mode 100755 _next/static/vJuFcpWvbN6zbAub4gcIZ/_buildManifest.js create mode 100755 _next/static/vJuFcpWvbN6zbAub4gcIZ/_ssgManifest.js diff --git a/404.html b/404.html index f50ae9e9..15116c8b 100755 --- a/404.html +++ b/404.html @@ -1 +1 @@ -404: This page could not be found.Albumentations: fast and flexible image augmentations

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.Albumentations: fast and flexible image augmentations

404

This page could not be found.

\ No newline at end of file diff --git a/404/index.html b/404/index.html index f50ae9e9..15116c8b 100755 --- a/404/index.html +++ b/404/index.html @@ -1 +1 @@ -404: This page could not be found.Albumentations: fast and flexible image augmentations

404

This page could not be found.

\ No newline at end of file +404: This page could not be found.Albumentations: fast and flexible image augmentations

404

This page could not be found.

\ No newline at end of file diff --git a/_next/static/vJuFcpWvbN6zbAub4gcIZ/_buildManifest.js b/_next/static/vJuFcpWvbN6zbAub4gcIZ/_buildManifest.js new file mode 100755 index 00000000..961beef3 --- /dev/null +++ b/_next/static/vJuFcpWvbN6zbAub4gcIZ/_buildManifest.js @@ -0,0 +1 @@ +self.__BUILD_MANIFEST=function(e,r,t){return{__rewrites:{afterFiles:[],beforeFiles:[{has:void 0,source:"//_next/:path+",destination:"/_next/:path+"}],fallback:[]},__routerFilterStatic:{numItems:5,errorRate:1e-4,numBits:96,numHashes:14,bitArray:[1,0,1,0,1,0,e,e,e,e,r,r,r,r,r,e,r,r,r,r,e,r,r,r,e,e,r,r,r,r,e,r,e,e,r,r,e,e,e,r,r,e,e,e,e,e,e,r,e,e,r,e,e,r,r,e,r,e,r,r,r,r,r,r,e,r,e,e,r,r,e,r,e,e,r,r,e,e,r,e,e,r,r,e,e,e,e,r,e,e,e,e,e,r,r,r]},__routerFilterDynamic:{numItems:e,errorRate:1e-4,numBits:e,numHashes:null,bitArray:[]},"/_error":["static/chunks/pages/_error-9b7125ad1a1e68fa.js"],sortedPages:["/_app","/_error"]}}(0,1,0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); \ No newline at end of file diff --git a/_next/static/vJuFcpWvbN6zbAub4gcIZ/_ssgManifest.js b/_next/static/vJuFcpWvbN6zbAub4gcIZ/_ssgManifest.js new file mode 100755 index 00000000..5b3ff592 --- /dev/null +++ b/_next/static/vJuFcpWvbN6zbAub4gcIZ/_ssgManifest.js @@ -0,0 +1 @@ +self.__SSG_MANIFEST=new Set([]);self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB() \ No newline at end of file diff --git a/docs/api_reference/augmentations/crops/transforms/index.html b/docs/api_reference/augmentations/crops/transforms/index.html index a40cd5c6..3c8a45d2 100644 --- a/docs/api_reference/augmentations/crops/transforms/index.html +++ b/docs/api_reference/augmentations/crops/transforms/index.html @@ -61,7 +61,7 @@ if len(bboxes) > 0: # Pick a bbox amongst all possible as our reference bbox. - bboxes = denormalize_bboxes(bboxes, image_shape=(image_height, image_width)) + bboxes = denormalize_bboxes(bboxes, shape=(image_height, image_width)) bbox = self.py_random.choice(bboxes) x1, y1, x2, y2 = bbox[:4] @@ -226,7 +226,7 @@ def get_transform_init_args_names(self) -> tuple[str, ...]: return ("erosion_rate",) -

class BaseCrop [view source on GitHub] ¶

Base class for transforms that only perform cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py
Python
class BaseCrop(DualTransform):
+

class BaseCrop [view source on GitHub] ¶

Base class for transforms that only perform cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py
Python
class BaseCrop(DualTransform):
     """Base class for transforms that only perform cropping."""
 
     _targets = ALL_TARGETS
diff --git a/docs/api_reference/augmentations/domain_adaptation/transforms/index.html b/docs/api_reference/augmentations/domain_adaptation/transforms/index.html
index 0f2801ff..969eb35a 100644
--- a/docs/api_reference/augmentations/domain_adaptation/transforms/index.html
+++ b/docs/api_reference/augmentations/domain_adaptation/transforms/index.html
@@ -6,7 +6,7 @@
   .jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon {
     font-size: 0.8rem;
   }
-    

Domain Adaptation transforms (augmentations.domain_adaptation.transforms)

class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub] ¶

Fourier Domain Adaptation (FDA) for simple "style transfer" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description
reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import numpy as np
+    

Domain Adaptation transforms (augmentations.domain_adaptation.transforms)

class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub] ¶

Fourier Domain Adaptation (FDA) for simple "style transfer" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description
reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
 >>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
@@ -111,7 +111,7 @@
     def to_dict_private(self) -> dict[str, Any]:
         msg = "FDA can not be serialized."
         raise NotImplementedError(msg)
-

class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub] ¶

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description
reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np
+

class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub] ¶

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description
reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
 >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
@@ -224,7 +224,7 @@
     def to_dict_private(self) -> dict[str, Any]:
         msg = "HistogramMatching can not be serialized."
         raise NotImplementedError(msg)
-

class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub] ¶

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description
reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal["pca", "standard", "minmax"]

Specifies the type of statistical transformation to apply. - "pca": Principal Component Analysis - "standard": StandardScaler (zero mean and unit variance) - "minmax": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: "pca"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np
+

class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub] ¶

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description
reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal["pca", "standard", "minmax"]

Specifies the type of statistical transformation to apply. - "pca": Principal Component Analysis - "standard": StandardScaler (zero mean and unit variance) - "minmax": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: "pca"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
 >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)
diff --git a/docs/api_reference/augmentations/dropout/xy_masking/index.html b/docs/api_reference/augmentations/dropout/xy_masking/index.html
index 01609ce4..d656e8e7 100644
--- a/docs/api_reference/augmentations/dropout/xy_masking/index.html
+++ b/docs/api_reference/augmentations/dropout/xy_masking/index.html
@@ -56,8 +56,8 @@
         mask_x_length: NonNegativeIntRangeType
         mask_y_length: NonNegativeIntRangeType
 
-        fill_value: DropoutFillValue | None = Field(deprecated="Deprecated use fill instead")
-        mask_fill_value: ColorType | None = Field(deprecated="Deprecated use fill_mask instead")
+        fill_value: DropoutFillValue | None
+        mask_fill_value: ColorType | None
 
         fill: DropoutFillValue
         fill_mask: ColorType | None
@@ -74,110 +74,112 @@
                 raise ValueError(msg)
 
             if self.fill_value is not None:
-                self.fill = self.fill_value
-
-            if self.mask_fill_value is not None:
-                self.fill_mask = self.mask_fill_value
-
-            return self
+                warn("fill_value is deprecated, use fill instead", DeprecationWarning, stacklevel=2)
+                self.fill = self.fill_value
+
+            if self.mask_fill_value is not None:
+                warn("mask_fill_value is deprecated, use fill_mask instead", DeprecationWarning, stacklevel=2)
+                self.fill_mask = self.mask_fill_value
 
-    def __init__(
-        self,
-        num_masks_x: ScaleIntType = 0,
-        num_masks_y: ScaleIntType = 0,
-        mask_x_length: ScaleIntType = 0,
-        mask_y_length: ScaleIntType = 0,
-        fill_value: DropoutFillValue | None = None,
-        mask_fill_value: ColorType | None = None,
-        fill: DropoutFillValue = 0,
-        fill_mask: ColorType | None = None,
-        p: float = 0.5,
-        always_apply: bool | None = None,
-    ):
-        super().__init__(p=p, fill=fill, fill_mask=fill_mask)
-        self.num_masks_x = cast(tuple[int, int], num_masks_x)
-        self.num_masks_y = cast(tuple[int, int], num_masks_y)
-
-        self.mask_x_length = cast(tuple[int, int], mask_x_length)
-        self.mask_y_length = cast(tuple[int, int], mask_y_length)
-
-    def validate_mask_length(
-        self,
-        mask_length: tuple[int, int] | None,
-        dimension_size: int,
-        dimension_name: str,
-    ) -> None:
-        """Validate the mask length against the corresponding image dimension size."""
-        if mask_length is not None:
-            if isinstance(mask_length, (tuple, list)):
-                if mask_length[0] < 0 or mask_length[1] > dimension_size:
-                    raise ValueError(
-                        f"{dimension_name} range {mask_length} is out of valid range [0, {dimension_size}]",
-                    )
-            elif mask_length < 0 or mask_length > dimension_size:
-                raise ValueError(f"{dimension_name} {mask_length} exceeds image {dimension_name} {dimension_size}")
-
-    def get_params_dependent_on_data(
-        self,
-        params: dict[str, Any],
-        data: dict[str, Any],
-    ) -> dict[str, np.ndarray]:
-        image_shape = params["shape"][:2]
-
-        height, width = image_shape
+            return self
+
+    def __init__(
+        self,
+        num_masks_x: ScaleIntType = 0,
+        num_masks_y: ScaleIntType = 0,
+        mask_x_length: ScaleIntType = 0,
+        mask_y_length: ScaleIntType = 0,
+        fill_value: DropoutFillValue | None = None,
+        mask_fill_value: ColorType | None = None,
+        fill: DropoutFillValue = 0,
+        fill_mask: ColorType | None = None,
+        p: float = 0.5,
+        always_apply: bool | None = None,
+    ):
+        super().__init__(p=p, fill=fill, fill_mask=fill_mask)
+        self.num_masks_x = cast(tuple[int, int], num_masks_x)
+        self.num_masks_y = cast(tuple[int, int], num_masks_y)
+
+        self.mask_x_length = cast(tuple[int, int], mask_x_length)
+        self.mask_y_length = cast(tuple[int, int], mask_y_length)
+
+    def validate_mask_length(
+        self,
+        mask_length: tuple[int, int] | None,
+        dimension_size: int,
+        dimension_name: str,
+    ) -> None:
+        """Validate the mask length against the corresponding image dimension size."""
+        if mask_length is not None:
+            if isinstance(mask_length, (tuple, list)):
+                if mask_length[0] < 0 or mask_length[1] > dimension_size:
+                    raise ValueError(
+                        f"{dimension_name} range {mask_length} is out of valid range [0, {dimension_size}]",
+                    )
+            elif mask_length < 0 or mask_length > dimension_size:
+                raise ValueError(f"{dimension_name} {mask_length} exceeds image {dimension_name} {dimension_size}")
+
+    def get_params_dependent_on_data(
+        self,
+        params: dict[str, Any],
+        data: dict[str, Any],
+    ) -> dict[str, np.ndarray]:
+        image_shape = params["shape"][:2]
 
-        self.validate_mask_length(self.mask_x_length, width, "mask_x_length")
-        self.validate_mask_length(self.mask_y_length, height, "mask_y_length")
-
-        masks_x = self.generate_masks(self.num_masks_x, image_shape, self.mask_x_length, axis="x")
-        masks_y = self.generate_masks(self.num_masks_y, image_shape, self.mask_y_length, axis="y")
-
-        holes = np.array(masks_x + masks_y)
+        height, width = image_shape
+
+        self.validate_mask_length(self.mask_x_length, width, "mask_x_length")
+        self.validate_mask_length(self.mask_y_length, height, "mask_y_length")
+
+        masks_x = self.generate_masks(self.num_masks_x, image_shape, self.mask_x_length, axis="x")
+        masks_y = self.generate_masks(self.num_masks_y, image_shape, self.mask_y_length, axis="y")
 
-        return {"holes": holes, "seed": self.random_generator.integers(0, 2**32 - 1)}
+        holes = np.array(masks_x + masks_y)
 
-    def generate_mask_size(self, mask_length: tuple[int, int]) -> int:
-        return self.py_random.randint(*mask_length)
-
-    def generate_masks(
-        self,
-        num_masks: tuple[int, int],
-        image_shape: tuple[int, int],
-        max_length: tuple[int, int] | None,
-        axis: str,
-    ) -> list[tuple[int, int, int, int]]:
-        if max_length is None or max_length == 0 or (isinstance(num_masks, (int, float)) and num_masks == 0):
-            return []
-
-        masks = []
-        num_masks_integer = (
-            num_masks if isinstance(num_masks, int) else self.py_random.randint(num_masks[0], num_masks[1])
-        )
-
-        height, width = image_shape
+        return {"holes": holes, "seed": self.random_generator.integers(0, 2**32 - 1)}
+
+    def generate_mask_size(self, mask_length: tuple[int, int]) -> int:
+        return self.py_random.randint(*mask_length)
+
+    def generate_masks(
+        self,
+        num_masks: tuple[int, int],
+        image_shape: tuple[int, int],
+        max_length: tuple[int, int] | None,
+        axis: str,
+    ) -> list[tuple[int, int, int, int]]:
+        if max_length is None or max_length == 0 or (isinstance(num_masks, (int, float)) and num_masks == 0):
+            return []
+
+        masks = []
+        num_masks_integer = (
+            num_masks if isinstance(num_masks, int) else self.py_random.randint(num_masks[0], num_masks[1])
+        )
 
-        for _ in range(num_masks_integer):
-            length = self.generate_mask_size(max_length)
-
-            if axis == "x":
-                x_min = self.py_random.randint(0, width - length)
-                y_min = 0
-                x_max, y_max = x_min + length, height
-            else:  # axis == 'y'
-                y_min = self.py_random.randint(0, height - length)
-                x_min = 0
-                x_max, y_max = width, y_min + length
-
-            masks.append((x_min, y_min, x_max, y_max))
-        return masks
-
-    def get_transform_init_args_names(self) -> tuple[str, ...]:
-        return (
-            "num_masks_x",
-            "num_masks_y",
-            "mask_x_length",
-            "mask_y_length",
-            "fill",
-            "fill_mask",
-        )
+        height, width = image_shape
+
+        for _ in range(num_masks_integer):
+            length = self.generate_mask_size(max_length)
+
+            if axis == "x":
+                x_min = self.py_random.randint(0, width - length)
+                y_min = 0
+                x_max, y_max = x_min + length, height
+            else:  # axis == 'y'
+                y_min = self.py_random.randint(0, height - length)
+                x_min = 0
+                x_max, y_max = width, y_min + length
+
+            masks.append((x_min, y_min, x_max, y_max))
+        return masks
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return (
+            "num_masks_x",
+            "num_masks_y",
+            "mask_x_length",
+            "mask_y_length",
+            "fill",
+            "fill_mask",
+        )
 
\ No newline at end of file diff --git a/docs/api_reference/augmentations/geometric/functional/index.html b/docs/api_reference/augmentations/geometric/functional/index.html index 2368df08..92de180f 100644 --- a/docs/api_reference/augmentations/geometric/functional/index.html +++ b/docs/api_reference/augmentations/geometric/functional/index.html @@ -6,7 +6,7 @@ .jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon { font-size: 0.8rem; } -

Geometric functional transforms (augmentations.geometric.functional)

def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]¶

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py
Python
def adjust_padding_by_position(
+    

Geometric functional transforms (augmentations.geometric.functional)

def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]¶

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py
Python
def adjust_padding_by_position(
     h_top: int,
     h_bottom: int,
     w_left: int,
@@ -40,7 +40,7 @@
         return h_top, h_bottom, w_left, w_right
 
     raise ValueError(f"Unknown position: {position}")
-

def almost_equal_intervals (n, parts) [view source on GitHub]¶

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description
n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description
np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)
+

def almost_equal_intervals (n, parts) [view source on GitHub]¶

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description
n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description
np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)
 array([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6
 >>> almost_equal_intervals(16, 4)
 array([4, 4, 4, 4])  # Splits 16 into four equal parts
@@ -69,7 +69,7 @@
     return np.array(
         [part_size + 1 if i < remainder else part_size for i in range(parts)],
     )
-

def apply_affine_to_points (points, matrix) [view source on GitHub]¶

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description
points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description
np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("points")
+

def apply_affine_to_points (points, matrix) [view source on GitHub]¶

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description
points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description
np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("points")
 def apply_affine_to_points(points: np.ndarray, matrix: np.ndarray) -> np.ndarray:
     """Apply affine transformation to a set of points.
 
@@ -95,7 +95,7 @@
     )
 
     return transformed_points[:, :2] / transformed_points[:, 2:]
-

def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]¶

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description
bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description
np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]¶

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description
bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description
np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_affine(
     bboxes: np.ndarray,
     matrix: np.ndarray,
@@ -163,7 +163,7 @@
     validated_bboxes = validate_bboxes(transformed_bboxes, output_shape)
 
     return normalize_bboxes(validated_bboxes, output_shape)
-

def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]¶

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]¶

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_affine_ellipse(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:
     """Apply an affine transformation to bounding boxes using an ellipse approximation method.
 
@@ -218,7 +218,7 @@
     new_y_max = np.max(transformed_points[:, :, 1], axis=1)
 
     return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])
-

def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]¶

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called "largest box" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels
+

def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]¶

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called "largest box" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels
 >>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)
 >>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)
 >>> print(transformed_bboxes)
@@ -276,7 +276,7 @@
     new_y_max = np.max(transformed_corners[:, :, 1], axis=1)
 
     return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])
-

def bboxes_d4 (bboxes, group_member) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def bboxes_d4 (bboxes, group_member) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_d4(
     bboxes: np.ndarray,
     group_member: D4Type,
@@ -322,7 +322,7 @@
         return transformations[group_member](bboxes)
 
     raise ValueError(f"Invalid group member: {group_member}")
-

def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]¶

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description
np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles
+

def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]¶

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description
np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles
 >>> tiles = np.array([
 ...     [0, 0, 50, 50],    # top-left tile
 ...     [0, 50, 50, 100],  # top-right tile
@@ -443,7 +443,7 @@
         return np.zeros((0, bboxes.shape[1]), dtype=bboxes.dtype)
 
     return shuffled_bboxes
-

def bboxes_hflip (bboxes) [view source on GitHub]¶

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def bboxes_hflip (bboxes) [view source on GitHub]¶

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_hflip(bboxes: np.ndarray) -> np.ndarray:
     """Flip bounding boxes horizontally around the y-axis.
 
@@ -459,8 +459,8 @@
     flipped_bboxes[:, 2] = 1 - bboxes[:, 0]  # new x_max = 1 - x_min
 
     return flipped_bboxes
-

def bboxes_rot90 (bboxes, factor) [view source on GitHub]¶

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor int

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description
np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Exceptions:

Type Description
ValueError

If factor is not in set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
-def bboxes_rot90(bboxes: np.ndarray, factor: int) -> np.ndarray:
+

def bboxes_rot90 (bboxes, factor) [view source on GitHub]¶

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor Literal[0, 1, 2, 3]

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description
np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+def bboxes_rot90(bboxes: np.ndarray, factor: Literal[0, 1, 2, 3]) -> np.ndarray:
     """Rotates bounding boxes by 90 degrees CCW (see np.rot90)
 
     Args:
@@ -470,37 +470,31 @@
 
     Returns:
         np.ndarray: A numpy array of rotated bounding boxes with the same shape as input.
-
-    Raises:
-        ValueError: If factor is not in set {0, 1, 2, 3}.
-    """
-    if factor not in {0, 1, 2, 3}:
-        raise ValueError("Parameter factor must be in set {0, 1, 2, 3}")
+    """
+    if factor == 0:
+        return bboxes
+
+    rotated_bboxes = bboxes.copy()
+    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]
 
-    if factor == 0:
-        return bboxes
-
-    rotated_bboxes = bboxes.copy()
-    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]
-
-    if factor == 1:
-        rotated_bboxes[:, 0] = y_min
-        rotated_bboxes[:, 1] = 1 - x_max
-        rotated_bboxes[:, 2] = y_max
-        rotated_bboxes[:, 3] = 1 - x_min
-    elif factor == ROT90_180_FACTOR:
-        rotated_bboxes[:, 0] = 1 - x_max
-        rotated_bboxes[:, 1] = 1 - y_max
-        rotated_bboxes[:, 2] = 1 - x_min
-        rotated_bboxes[:, 3] = 1 - y_min
-    elif factor == ROT90_270_FACTOR:
-        rotated_bboxes[:, 0] = 1 - y_max
-        rotated_bboxes[:, 1] = x_min
-        rotated_bboxes[:, 2] = 1 - y_min
-        rotated_bboxes[:, 3] = x_max
-
-    return rotated_bboxes
-

def bboxes_transpose (bboxes) [view source on GitHub]¶

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+    if factor == 1:
+        rotated_bboxes[:, 0] = y_min
+        rotated_bboxes[:, 1] = 1 - x_max
+        rotated_bboxes[:, 2] = y_max
+        rotated_bboxes[:, 3] = 1 - x_min
+    elif factor == ROT90_180_FACTOR:
+        rotated_bboxes[:, 0] = 1 - x_max
+        rotated_bboxes[:, 1] = 1 - y_max
+        rotated_bboxes[:, 2] = 1 - x_min
+        rotated_bboxes[:, 3] = 1 - y_min
+    elif factor == ROT90_270_FACTOR:
+        rotated_bboxes[:, 0] = 1 - y_max
+        rotated_bboxes[:, 1] = x_min
+        rotated_bboxes[:, 2] = 1 - y_min
+        rotated_bboxes[:, 3] = x_max
+
+    return rotated_bboxes
+

def bboxes_transpose (bboxes) [view source on GitHub]¶

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_transpose(bboxes: np.ndarray) -> np.ndarray:
     """Transpose bounding boxes by swapping x and y coordinates.
 
@@ -515,7 +509,7 @@
     transposed_bboxes[:, [0, 1, 2, 3]] = bboxes[:, [1, 0, 3, 2]]
 
     return transposed_bboxes
-

def bboxes_vflip (bboxes) [view source on GitHub]¶

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def bboxes_vflip (bboxes) [view source on GitHub]¶

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description
np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def bboxes_vflip(bboxes: np.ndarray) -> np.ndarray:
     """Flip bounding boxes vertically around the x-axis.
 
@@ -531,7 +525,7 @@
     flipped_bboxes[:, 3] = 1 - bboxes[:, 1]  # new y_max = 1 - y_min
 
     return flipped_bboxes
-

def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]¶

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py
Python
def calculate_affine_transform_padding(
+

def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]¶

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py
Python
def calculate_affine_transform_padding(
     matrix: np.ndarray,
     image_shape: tuple[int, int],
 ) -> tuple[int, int, int, int]:
@@ -577,7 +571,7 @@
     pad_bottom = max(0, math.ceil(max_y - height))
 
     return pad_left, pad_right, pad_top, pad_bottom
-

def center (image_shape) [view source on GitHub]¶

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description
tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py
Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:
+

def center (image_shape) [view source on GitHub]¶

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description
tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py
Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:
     """Calculate the center coordinates if image. Used by images, masks and keypoints.
 
     Args:
@@ -588,7 +582,7 @@
     """
     height, width = image_shape[:2]
     return width / 2 - 0.5, height / 2 - 0.5
-

def center_bbox (image_shape) [view source on GitHub]¶

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description
tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py
Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:
+

def center_bbox (image_shape) [view source on GitHub]¶

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description
tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py
Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:
     """Calculate the center coordinates for of image for bounding boxes.
 
     Args:
@@ -599,7 +593,7 @@
     """
     height, width = image_shape[:2]
     return width / 2, height / 2
-

def compute_tps_weights (src_points, dst_points) [view source on GitHub]¶

Compute Thin Plate Spline weights.

Parameters:

Name Type Description
src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description
tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py
Python
def compute_tps_weights(
+

def compute_tps_weights (src_points, dst_points) [view source on GitHub]¶

Compute Thin Plate Spline weights.

Parameters:

Name Type Description
src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description
tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py
Python
def compute_tps_weights(
     src_points: np.ndarray,
     dst_points: np.ndarray,
 ) -> tuple[np.ndarray, np.ndarray]:
@@ -655,7 +649,7 @@
     affine_weights = all_weights[num_points:]
 
     return nonlinear_weights, affine_weights
-

def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]¶

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description
matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description
tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py
Python
def compute_transformed_image_bounds(
+

def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]¶

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description
matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description
tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py
Python
def compute_transformed_image_bounds(
     matrix: np.ndarray,
     image_shape: tuple[int, int],
 ) -> tuple[np.ndarray, np.ndarray]:
@@ -684,7 +678,7 @@
     max_coords = np.ceil(transformed_corners.max(axis=0)).astype(int)
 
     return min_coords, max_coords
-

def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]¶

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description
translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description
np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_affine_transformation_matrix(
+

def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]¶

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description
translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description
np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_affine_transformation_matrix(
     translate: XYInt,
     shear: XYFloat,
     scale: XYFloat,
@@ -744,7 +738,7 @@
     m[2] = [0, 0, 1]
 
     return m
-

def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]¶

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_piecewise_affine_maps(
+

def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]¶

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_piecewise_affine_maps(
     image_shape: tuple[int, int],
     grid: tuple[int, int],
     scale: float,
@@ -815,14 +809,14 @@
     map_y = np.clip(map_y, 0, height - 1, out=map_y)
 
     return map_x, map_y
-

def create_shape_groups (tiles) [view source on GitHub]¶

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:
+

def create_shape_groups (tiles) [view source on GitHub]¶

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py
Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:
     """Groups tiles by their shape and stores the indices for each shape."""
     shape_groups = defaultdict(list)
     for index, (start_y, start_x, end_y, end_x) in enumerate(tiles):
         shape = (end_y - start_y, end_x - start_x)
         shape_groups[shape].append(index)
     return shape_groups
-

def d4 (img, group_member) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py
Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:
+

def d4 (img, group_member) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py
Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:
     """Applies a `D_4` symmetry group transformation to an image array.
 
     This function manipulates an image using transformations such as rotations and flips,
@@ -869,7 +863,7 @@
         return transformations[group_member](img)
 
     raise ValueError(f"Invalid group member: {group_member}")
-

def distort_image (image, generated_mesh, interpolation) [view source on GitHub]¶

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description
image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description
np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
+

def distort_image (image, generated_mesh, interpolation) [view source on GitHub]¶

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description
image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description
np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)
 >>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])
 >>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)
 >>> distorted.shape
@@ -949,7 +943,7 @@
         distorted_image = cv2.copyTo(warped, mask, distorted_image)
 
     return distorted_image
-

def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]¶

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py
Python
def find_keypoint(
+

def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]¶

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py
Python
def find_keypoint(
     position: tuple[int, int],
     distance_map: np.ndarray,
     threshold: float | None,
@@ -963,7 +957,7 @@
     if inverted and threshold is not None and value <= threshold:
         return None
     return float(x), float(y)
-

def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]¶

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
+

def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]¶

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("bboxes")
 def flip_bboxes(
     bboxes: np.ndarray,
     flip_horizontal: bool = False,
@@ -989,7 +983,7 @@
     if flip_vertical:
         flipped_bboxes[:, [1, 3]] = rows - flipped_bboxes[:, [3, 1]]
     return flipped_bboxes
-

def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]¶

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description
distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description
np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description
ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints
+

def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]¶

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description
distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description
np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description
ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints
 >>> inverted = True
 >>> if_not_found_coords = [0, 0]
 >>> threshold = 0.5
@@ -1089,7 +1083,7 @@
             return keypoints[valid_mask]
 
     return keypoints
-

def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]¶

Generate displacement fields for elastic transform.

Parameters:

Name Type Description
image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use ("gaussian" or "uniform")

Returns:

Type Description
tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_displacement_fields(
+

def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]¶

Generate displacement fields for elastic transform.

Parameters:

Name Type Description
image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use ("gaussian" or "uniform")

Returns:

Type Description
tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_displacement_fields(
     image_shape: tuple[int, int],
     alpha: float,
     sigma: float,
@@ -1132,7 +1126,7 @@
     dy = dx if same_dxdy else generate_noise_field()
 
     return dx, dy
-

def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]¶

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description
dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description
np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],
+

def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]¶

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description
dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description
np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],
 ...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])
 >>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)
 >>> distorted.shape
@@ -1223,7 +1217,7 @@
             polygons[i * grid_width + j, 0:2] += [dx, dy]
 
     return polygons
-

def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]¶

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description
tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)
+

def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]¶

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description
tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)
 >>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]
 >>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]
 >>> num_steps = 5
@@ -1300,7 +1294,7 @@
         prev = cur
 
     return np.meshgrid(xx, yy)
-

def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]¶

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description
bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description
np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_reflected_bboxes(
+

def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]¶

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description
bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description
np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_reflected_bboxes(
     bboxes: np.ndarray,
     grid_dims: dict[str, tuple[int, int]],
     image_shape: tuple[int, int],
@@ -1375,7 +1369,7 @@
     result = np.vstack(new_bboxes)
 
     return shift_bboxes(result, -shift_vector) if center_in_origin else result
-

def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]¶

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description
keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - "grid_shape": tuple[int, int] representing (grid_rows, grid_cols) - "original_position": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description
np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_reflected_keypoints(
+

def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]¶

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description
keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - "grid_shape": tuple[int, int] representing (grid_rows, grid_cols) - "original_position": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description
np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_reflected_keypoints(
     keypoints: np.ndarray,
     grid_dims: dict[str, tuple[int, int]],
     image_shape: tuple[int, int],
@@ -1430,7 +1424,7 @@
 
     # Shift all versions to the original position
     shift_vector = np.array(
-        [original_col * cols, original_row * rows, 0, 0],
+        [original_col * cols, original_row * rows, 0, 0, 0],
     )  # Only shift x and y
     keypoints = shift_keypoints(keypoints, shift_vector)
     keypoints_hflipped = shift_keypoints(keypoints_hflipped, shift_vector)
@@ -1458,16 +1452,17 @@
                     (grid_row - original_row) * rows,
                     0,
                     0,
-                ],
-            )
-            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)
-
-            new_keypoints.append(shifted_keypoints)
-
-    result = np.vstack(new_keypoints)
-
-    return shift_keypoints(result, -shift_vector) if center_in_origin else result
-

def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]¶

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description
size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description
np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_shuffled_splits(
+                    0,
+                ],
+            )
+            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)
+
+            new_keypoints.append(shifted_keypoints)
+
+    result = np.vstack(new_keypoints)
+
+    return shift_keypoints(result, -shift_vector) if center_in_origin else result
+

def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]¶

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description
size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description
np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py
Python
def generate_shuffled_splits(
     size: int,
     divisions: int,
     random_generator: np.random.Generator,
@@ -1486,7 +1481,7 @@
     intervals = almost_equal_intervals(size, divisions)
     random_generator.shuffle(intervals)
     return np.insert(np.cumsum(intervals), 0, 0)
-

def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]¶

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description
image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description
tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py
Python
def get_camera_matrix_distortion_maps(
+

def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]¶

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description
image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description
tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py
Python
def get_camera_matrix_distortion_maps(
     image_shape: tuple[int, int],
     k: float,
     center_xy: tuple[float, float],
@@ -1516,7 +1511,7 @@
         (width, height),
         cv2.CV_32FC1,
     )
-

def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]¶

Calculate padding for a single dimension.

Parameters:

Name Type Description
current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description
tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_dimension_padding(
+

def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]¶

Calculate padding for a single dimension.

Parameters:

Name Type Description
current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description
tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_dimension_padding(
     current_size: int,
     min_size: int | None,
     divisor: int | None,
@@ -1545,7 +1540,7 @@
             return pad_before, pad_after
 
     return 0, 0
-

def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]¶

Generate distortion maps using fisheye model.

Parameters:

Name Type Description
image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description
tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py
Python
def get_fisheye_distortion_maps(
+

def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]¶

Generate distortion maps using fisheye model.

Parameters:

Name Type Description
image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description
tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py
Python
def get_fisheye_distortion_maps(
     image_shape: tuple[int, int],
     k: float,
     center_xy: tuple[float, float],
@@ -1587,7 +1582,7 @@
     map_y = r_dist * np.sin(theta) + center_y
 
     return map_x, map_y
-

def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]¶

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description
pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description
dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_pad_grid_dimensions(
+

def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]¶

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description
pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description
dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_pad_grid_dimensions(
     pad_top: int,
     pad_bottom: int,
     pad_left: int,
@@ -1623,7 +1618,7 @@
         "grid_shape": (grid_rows, grid_cols),
         "original_position": (original_row, original_col),
     }
-

def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]¶

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description
image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description
tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_padding_params(
+

def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]¶

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description
image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description
tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py
Python
def get_padding_params(
     image_shape: tuple[int, int],
     min_height: int | None,
     min_width: int | None,
@@ -1652,7 +1647,7 @@
     w_pad_left, w_pad_right = get_dimension_padding(cols, min_width, pad_width_divisor)
 
     return h_pad_top, h_pad_bottom, w_pad_left, w_pad_right
-

def is_identity_matrix (matrix) [view source on GitHub]¶

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description
matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description
bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py
Python
def is_identity_matrix(matrix: np.ndarray) -> bool:
+

def is_identity_matrix (matrix) [view source on GitHub]¶

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description
matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description
bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py
Python
def is_identity_matrix(matrix: np.ndarray) -> bool:
     """Check if the given matrix is an identity matrix.
 
     Args:
@@ -1662,7 +1657,7 @@
         bool: True if the matrix is an identity matrix, False otherwise.
     """
     return np.allclose(matrix, np.eye(3, dtype=matrix.dtype))
-

def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]¶

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py
Python
def is_valid_component(
+

def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]¶

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py
Python
def is_valid_component(
     component_area: float,
     original_area: float,
     min_area: float | None,
@@ -1671,7 +1666,7 @@
     """Validate if a component meets the minimum requirements."""
     visibility = component_area / original_area
     return (min_area is None or component_area >= min_area) and (min_visibility is None or visibility >= min_visibility)
-

def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]¶

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description
np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2Ï€] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])
+

def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]¶

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description
np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2Ï€] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])
 >>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])
 >>> scale = {'x': 1.5, 'y': 1.2}
 >>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)
@@ -1740,7 +1735,7 @@
             center_in_origin=True,
         )
 
-    # Extract x, y coordinates
+    # Extract x, y coordinates (z is preserved)
     xy = keypoints[:, :2]
 
     # Ensure matrix is 2x3
@@ -1753,19 +1748,18 @@
     # Calculate angle adjustment
     angle_adjustment = rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=False)
 
-    # Update angles
-    keypoints[:, 2] = keypoints[:, 2] + angle_adjustment
+    # Update angles (now at index 3)
+    keypoints[:, 3] = keypoints[:, 3] + angle_adjustment
 
-    # Update scales
+    # Update scales (now at index 4)
     max_scale = max(scale["x"], scale["y"])
-
-    keypoints[:, 3] *= max_scale
-
-    # Update x, y coordinates
-    keypoints[:, :2] = xy_transformed
-
-    return keypoints
-

def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+    keypoints[:, 4] *= max_scale
+
+    # Update x, y coordinates and preserve z
+    keypoints[:, :2] = xy_transformed
+
+    return keypoints
+

def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]¶

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 def keypoints_d4(
     keypoints: np.ndarray,
     group_member: D4Type,
@@ -1814,7 +1808,7 @@
         return transformations[group_member](keypoints)
 
     raise ValueError(f"Invalid group member: {group_member}")
-

def keypoints_hflip (keypoints, cols) [view source on GitHub]¶

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description
np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+

def keypoints_hflip (keypoints, cols) [view source on GitHub]¶

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description
np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 @angle_2pi_range
 def keypoints_hflip(keypoints: np.ndarray, cols: int) -> np.ndarray:
     """Flip keypoints horizontally around the y-axis.
@@ -1832,14 +1826,14 @@
     flipped_keypoints[:, 0] = (cols - 1) - keypoints[:, 0]
 
     # Adjust angles
-    flipped_keypoints[:, 2] = np.pi - keypoints[:, 2]
+    flipped_keypoints[:, 3] = np.pi - keypoints[:, 3]
 
     return flipped_keypoints
-

def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]¶

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description
keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description
np.ndarray

The rotated keypoints with the same shape as the input.

Exceptions:

Type Description
ValueError

If the factor is not in the set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+

def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]¶

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description
keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description
np.ndarray

The rotated keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 @angle_2pi_range
 def keypoints_rot90(
     keypoints: np.ndarray,
-    factor: int,
+    factor: Literal[0, 1, 2, 3],
     image_shape: tuple[int, int],
 ) -> np.ndarray:
     """Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.
@@ -1851,36 +1845,30 @@
 
     Returns:
         np.ndarray: The rotated keypoints with the same shape as the input.
-
-    Raises:
-        ValueError: If the factor is not in the set {0, 1, 2, 3}.
-    """
-    if factor not in {0, 1, 2, 3}:
-        raise ValueError("Parameter factor must be in set {0, 1, 2, 3}")
+    """
+    if factor == 0:
+        return keypoints
+
+    height, width = image_shape[:2]
+    rotated_keypoints = keypoints.copy().astype(np.float32)
 
-    if factor == 0:
-        return keypoints
-
-    height, width = image_shape[:2]
-    rotated_keypoints = keypoints.copy().astype(np.float32)
-
-    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2]
-
-    if factor == 1:
-        rotated_keypoints[:, 0] = y
-        rotated_keypoints[:, 1] = width - 1 - x
-        rotated_keypoints[:, 2] = angle - np.pi / 2
-    elif factor == ROT90_180_FACTOR:
-        rotated_keypoints[:, 0] = width - 1 - x
-        rotated_keypoints[:, 1] = height - 1 - y
-        rotated_keypoints[:, 2] = angle - np.pi
-    elif factor == ROT90_270_FACTOR:
-        rotated_keypoints[:, 0] = height - 1 - y
-        rotated_keypoints[:, 1] = x
-        rotated_keypoints[:, 2] = angle + np.pi / 2
-
-    return rotated_keypoints
-

def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]¶

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description
np.ndarray

A numpy array of scaled keypoints with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 3]
+
+    if factor == 1:
+        rotated_keypoints[:, 0] = y
+        rotated_keypoints[:, 1] = width - 1 - x
+        rotated_keypoints[:, 3] = angle - np.pi / 2
+    elif factor == ROT90_180_FACTOR:
+        rotated_keypoints[:, 0] = width - 1 - x
+        rotated_keypoints[:, 1] = height - 1 - y
+        rotated_keypoints[:, 3] = angle - np.pi
+    elif factor == ROT90_270_FACTOR:
+        rotated_keypoints[:, 0] = height - 1 - y
+        rotated_keypoints[:, 1] = x
+        rotated_keypoints[:, 3] = angle + np.pi / 2
+
+    return rotated_keypoints
+

def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]¶

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of keypoints with shape (N, 5+) in the format (x, y, z, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description
np.ndarray

A numpy array of scaled keypoints with the same shape as input. X and Y coordinates are scaled by their respective scale factors, Z coordinate remains unchanged, and the keypoint scale is multiplied by max(scale_x, scale_y).

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 def keypoints_scale(
     keypoints: np.ndarray,
     scale_x: float,
@@ -1889,39 +1877,44 @@
     """Scales keypoints by scale_x and scale_y.
 
     Args:
-        keypoints: A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).
-        scale_x: Scale coefficient x-axis.
-        scale_y: Scale coefficient y-axis.
-
-    Returns:
-        A numpy array of scaled keypoints with the same shape as input.
-    """
-    # Extract x, y, angle, and scale
-    x, y, angle, scale = (
-        keypoints[:, 0],
-        keypoints[:, 1],
-        keypoints[:, 2],
-        keypoints[:, 3],
-    )
-
-    # Scale x and y
-    x_scaled = x * scale_x
-    y_scaled = y * scale_y
-
-    # Scale the keypoint scale by the maximum of scale_x and scale_y
-    scale_scaled = scale * max(scale_x, scale_y)
-
-    # Create the output array
-    scaled_keypoints = np.column_stack([x_scaled, y_scaled, angle, scale_scaled])
-
-    # If there are additional columns, preserve them
-    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:
-        return np.column_stack(
-            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],
-        )
-
-    return scaled_keypoints
-

def keypoints_transpose (keypoints) [view source on GitHub]¶

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description
np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+        keypoints: A numpy array of keypoints with shape (N, 5+) in the format
+                  (x, y, z, angle, scale, ...).
+        scale_x: Scale coefficient x-axis.
+        scale_y: Scale coefficient y-axis.
+
+    Returns:
+        A numpy array of scaled keypoints with the same shape as input.
+        X and Y coordinates are scaled by their respective scale factors,
+        Z coordinate remains unchanged, and the keypoint scale is multiplied
+        by max(scale_x, scale_y).
+    """
+    # Extract x, y, z, angle, and scale
+    x, y, z, angle, scale = (
+        keypoints[:, 0],
+        keypoints[:, 1],
+        keypoints[:, 2],
+        keypoints[:, 3],
+        keypoints[:, 4],
+    )
+
+    # Scale x and y
+    x_scaled = x * scale_x
+    y_scaled = y * scale_y
+
+    # Scale the keypoint scale by the maximum of scale_x and scale_y
+    scale_scaled = scale * max(scale_x, scale_y)
+
+    # Create the output array
+    scaled_keypoints = np.column_stack([x_scaled, y_scaled, z, angle, scale_scaled])
+
+    # If there are additional columns, preserve them
+    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:
+        return np.column_stack(
+            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],
+        )
+
+    return scaled_keypoints
+

def keypoints_transpose (keypoints) [view source on GitHub]¶

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description
np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 @angle_2pi_range
 def keypoints_transpose(keypoints: np.ndarray) -> np.ndarray:
     """Transposes keypoints along the main diagonal.
@@ -1938,15 +1931,15 @@
     transposed_keypoints[:, [0, 1]] = keypoints[:, [1, 0]]
 
     # Adjust angles to reflect the coordinate swap
-    angles = keypoints[:, 2]
-    transposed_keypoints[:, 2] = np.where(
+    angles = keypoints[:, 3]
+    transposed_keypoints[:, 3] = np.where(
         angles <= np.pi,
         np.pi / 2 - angles,
         3 * np.pi / 2 - angles,
     )
 
     return transposed_keypoints
-

def keypoints_vflip (keypoints, rows) [view source on GitHub]¶

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description
np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+

def keypoints_vflip (keypoints, rows) [view source on GitHub]¶

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description
np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
 @angle_2pi_range
 def keypoints_vflip(keypoints: np.ndarray, rows: int) -> np.ndarray:
     """Flip keypoints vertically around the x-axis.
@@ -1964,10 +1957,10 @@
     flipped_keypoints[:, 1] = (rows - 1) - keypoints[:, 1]
 
     # Negate angles
-    flipped_keypoints[:, 2] = -keypoints[:, 2]
+    flipped_keypoints[:, 3] = -keypoints[:, 3]
 
     return flipped_keypoints
-

def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]¶

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])
+

def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]¶

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description
bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description
np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])
 >>> image_shape = (100, 100)
 >>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])
 >>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)
@@ -2043,7 +2036,83 @@
     transformed_bboxes[:, :4] = normalized_coords
 
     return transformed_bboxes
-

def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]¶

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py
Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:
+

def perspective_keypoints (keypoints, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]¶

Apply perspective transformation to keypoints.

Parameters:

Name Type Description
keypoints np.ndarray

Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].

image_shape tuple[int, int]

Original image shape (height, width).

matrix np.ndarray

3x3 perspective transformation matrix.

max_width int

Maximum width after transformation.

max_height int

Maximum height after transformation.

keep_size bool

Whether to keep original size.

Returns:

Type Description
np.ndarray

Transformed keypoints array with same shape as input. Z coordinate remains unchanged through the transformation.

Source code in albumentations/augmentations/geometric/functional.py
Python
@handle_empty_array("keypoints")
+@angle_2pi_range
+def perspective_keypoints(
+    keypoints: np.ndarray,
+    image_shape: tuple[int, int],
+    matrix: np.ndarray,
+    max_width: int,
+    max_height: int,
+    keep_size: bool,
+) -> np.ndarray:
+    """Apply perspective transformation to keypoints.
+
+    Args:
+        keypoints: Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].
+        image_shape: Original image shape (height, width).
+        matrix: 3x3 perspective transformation matrix.
+        max_width: Maximum width after transformation.
+        max_height: Maximum height after transformation.
+        keep_size: Whether to keep original size.
+
+    Returns:
+        Transformed keypoints array with same shape as input.
+        Z coordinate remains unchanged through the transformation.
+    """
+    keypoints = keypoints.copy().astype(np.float32)
+
+    height, width = image_shape[:2]
+
+    x, y, z, angle, scale = (
+        keypoints[:, 0],
+        keypoints[:, 1],
+        keypoints[:, 2],
+        keypoints[:, 3],
+        keypoints[:, 4],
+    )
+
+    # Reshape keypoints for perspective transform
+    keypoint_vector = np.column_stack((x, y)).astype(np.float32).reshape(-1, 1, 2)
+
+    # Apply perspective transform
+    transformed_points = cv2.perspectiveTransform(keypoint_vector, matrix).squeeze()
+
+    # Unsqueeze if we have a single keypoint
+    if transformed_points.ndim == 1:
+        transformed_points = transformed_points[np.newaxis, :]
+
+    x, y = transformed_points[:, 0], transformed_points[:, 1]
+
+    # Update angles
+    angle += rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=True)
+
+    # Calculate scale factors
+    scale_x = np.sign(matrix[0, 0]) * np.sqrt(matrix[0, 0] ** 2 + matrix[0, 1] ** 2)
+    scale_y = np.sign(matrix[1, 1]) * np.sqrt(matrix[1, 0] ** 2 + matrix[1, 1] ** 2)
+    scale *= max(scale_x, scale_y)
+
+    if keep_size:
+        scale_x = width / max_width
+        scale_y = height / max_height
+        x *= scale_x
+        y *= scale_y
+        scale *= max(scale_x, scale_y)
+
+    # Create the output array with unchanged z coordinate
+    transformed_keypoints = np.column_stack([x, y, z, angle, scale])
+
+    # If there are additional columns, preserve them
+    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:
+        return np.column_stack(
+            [
+                transformed_keypoints,
+                keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:],
+            ],
+        )
+
+    return transformed_keypoints
+

def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]¶

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py
Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:
     """Args:
     matrix (np.ndarray): Rotation matrix
     y_up (bool): is Y axis looks up or down
@@ -2052,7 +2121,7 @@
     if y_up:
         return np.arctan2(matrix[1, 0], matrix[0, 0])
     return np.arctan2(-matrix[1, 0], matrix[0, 0])
-

def shift_bboxes (bboxes, shift_vector) [view source on GitHub]¶

Shift bounding boxes by a given vector.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description
np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:
+

def shift_bboxes (bboxes, shift_vector) [view source on GitHub]¶

Shift bounding boxes by a given vector.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description
np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py
Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:
     """Shift bounding boxes by a given vector.
 
     Args:
@@ -2071,7 +2140,7 @@
     shifted_bboxes[:, :4] += shift_vector
 
     return shifted_bboxes
-

def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]¶

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description
shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description
list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py
Python
def shuffle_tiles_within_shape_groups(
+

def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]¶

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description
shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description
list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py
Python
def shuffle_tiles_within_shape_groups(
     shape_groups: dict[tuple[int, int], list[int]],
     random_generator: np.random.Generator,
 ) -> list[int]:
@@ -2100,7 +2169,7 @@
             mapping[old] = new
 
     return mapping
-

def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]¶

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description
np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py
Python
def split_uniform_grid(
+

def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]¶

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description
image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description
np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py
Python
def split_uniform_grid(
     image_shape: tuple[int, int],
     grid: tuple[int, int],
     random_generator: np.random.Generator,
@@ -2141,7 +2210,7 @@
     ]
 
     return np.array(tiles, dtype=np.int16)
-

def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]¶

Swap tiles on the image according to the new format.

Parameters:

Name Type Description
image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description
np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py
Python
def swap_tiles_on_image(
+

def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]¶

Swap tiles on the image according to the new format.

Parameters:

Name Type Description
image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description
np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py
Python
def swap_tiles_on_image(
     image: np.ndarray,
     tiles: np.ndarray,
     mapping: list[int] | None = None,
@@ -2172,7 +2241,7 @@
         ]
 
     return new_image
-

def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]¶

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description
keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description
np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description
RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py
Python
def swap_tiles_on_keypoints(
+

def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]¶

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description
keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description
np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description
RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py
Python
def swap_tiles_on_keypoints(
     keypoints: np.ndarray,
     tiles: np.ndarray,
     mapping: np.ndarray,
@@ -2244,7 +2313,7 @@
     new_keypoints[not_in_any_tile] = keypoints[not_in_any_tile]
 
     return new_keypoints
-

def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]¶

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description
np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py
Python
def to_distance_maps(
+

def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]¶

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description
np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py
Python
def to_distance_maps(
     keypoints: np.ndarray,
     image_shape: tuple[int, int],
     inverted: bool = False,
@@ -2292,7 +2361,7 @@
     if inverted:
         return (1 / (distances + 1)).astype(np.float32)
     return distances.astype(np.float32)
-

def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]¶

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description
target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description
np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py
Python
def tps_transform(
+

def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]¶

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description
target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description
np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py
Python
def tps_transform(
     target_points: np.ndarray,
     control_points: np.ndarray,
     nonlinear_weights: np.ndarray,
@@ -2329,7 +2398,7 @@
 
     # Combine nonlinear and affine transformations
     return kernel_matrix @ nonlinear_weights + affine_terms @ affine_weights
-

def transpose (img) [view source on GitHub]¶

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description
img np.ndarray

Input array.

Returns:

Type Description
np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py
Python
def transpose(img: np.ndarray) -> np.ndarray:
+

def transpose (img) [view source on GitHub]¶

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description
img np.ndarray

Input array.

Returns:

Type Description
np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py
Python
def transpose(img: np.ndarray) -> np.ndarray:
     """Transposes the first two dimensions of an array of any dimensionality.
     Retains the order of any additional dimensions.
 
@@ -2345,7 +2414,7 @@
 
     # Transpose the array using the new axes order
     return img.transpose(new_axes)
-

def validate_bboxes (bboxes, image_shape) [view source on GitHub]¶

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])
+

def validate_bboxes (bboxes, image_shape) [view source on GitHub]¶

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])
 >>> valid_bboxes = validate_bboxes(bboxes, (100, 100))
 >>> print(valid_bboxes)
 [[10 20 30 40]]
@@ -2372,7 +2441,7 @@
     valid_indices = (x_max > 0) & (y_max > 0) & (x_min < cols) & (y_min < rows)
 
     return bboxes[valid_indices]
-

def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]¶

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py
Python
def validate_if_not_found_coords(
+

def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]¶

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py
Python
def validate_if_not_found_coords(
     if_not_found_coords: Sequence[int] | dict[str, Any] | None,
 ) -> tuple[bool, float, float]:
     """Validate and process `if_not_found_coords` parameter."""
@@ -2388,7 +2457,7 @@
 
     msg = "Expected if_not_found_coords to be None, tuple, list, or dict."
     raise ValueError(msg)
-

def validate_keypoints (keypoints, image_shape) [view source on GitHub]¶

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py
Python
def validate_keypoints(
+

def validate_keypoints (keypoints, image_shape) [view source on GitHub]¶

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description
np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py
Python
def validate_keypoints(
     keypoints: np.ndarray,
     image_shape: tuple[int, int],
 ) -> np.ndarray:
diff --git a/docs/api_reference/augmentations/geometric/rotate/index.html b/docs/api_reference/augmentations/geometric/rotate/index.html
index 91c8c6bb..f96e35d6 100644
--- a/docs/api_reference/augmentations/geometric/rotate/index.html
+++ b/docs/api_reference/augmentations/geometric/rotate/index.html
@@ -22,7 +22,7 @@
 
     _targets = ALL_TARGETS
 
-    def apply(self, img: np.ndarray, factor: int, **params: Any) -> np.ndarray:
+    def apply(self, img: np.ndarray, factor: Literal[0, 1, 2, 3], **params: Any) -> np.ndarray:
         return fgeometric.rot90(img, factor)
 
     def get_params(self) -> dict[str, int]:
@@ -32,7 +32,7 @@
     def apply_to_bboxes(
         self,
         bboxes: np.ndarray,
-        factor: int,
+        factor: Literal[0, 1, 2, 3],
         **params: Any,
     ) -> np.ndarray:
         return fgeometric.bboxes_rot90(bboxes, factor)
@@ -40,7 +40,7 @@
     def apply_to_keypoints(
         self,
         keypoints: np.ndarray,
-        factor: int,
+        factor: Literal[0, 1, 2, 3],
         **params: Any,
     ) -> np.ndarray:
         return fgeometric.keypoints_rot90(keypoints, factor, params["shape"])
diff --git a/docs/api_reference/augmentations/geometric/transforms/index.html b/docs/api_reference/augmentations/geometric/transforms/index.html
index a0bef437..cc6284f9 100644
--- a/docs/api_reference/augmentations/geometric/transforms/index.html
+++ b/docs/api_reference/augmentations/geometric/transforms/index.html
@@ -1111,7 +1111,7 @@
 
     def get_transform_init_args_names(self) -> tuple[str, ...]:
         return "num_grid_xy", "magnitude", "interpolation", "mask_interpolation"
-

class HorizontalFlip [view source on GitHub] ¶

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description
p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py
Python
class HorizontalFlip(DualTransform):
+

class HorizontalFlip [view source on GitHub] ¶

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description
p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py
Python
class HorizontalFlip(DualTransform):
     """Flip the input horizontally around the y-axis.
 
     Args:
@@ -1679,173 +1679,172 @@
     class InitSchema(BaseTransformInitSchema):
         scale: NonNegativeFloatRangeType
         keep_size: bool
-        pad_mode: BorderModeType | None = Field(
-            deprecated="Deprecated use border_mode instead",
-        )
-        pad_val: ColorType | None = Field(deprecated="Deprecated use fill instead")
-        mask_pad_val: ColorType | None = Field(
-            deprecated="Deprecated use fill_mask instead",
-        )
-        fit_output: bool
-        interpolation: InterpolationType
-        mask_interpolation: InterpolationType
-        fill: ColorType
-        fill_mask: ColorType
-        border_mode: BorderModeType
-
-        @model_validator(mode="after")
-        def validate_deprecated_fields(self) -> Self:
-            if self.pad_mode is not None:
-                self.border_mode = self.pad_mode
-            if self.pad_val is not None:
-                self.fill = self.pad_val
-            if self.mask_pad_val is not None:
-                self.fill_mask = self.mask_pad_val
-            return self
-
-    def __init__(
-        self,
-        scale: ScaleFloatType = (0.05, 0.1),
-        keep_size: bool = True,
-        pad_mode: int | None = None,
-        pad_val: ColorType | None = None,
-        mask_pad_val: ColorType | None = None,
-        fit_output: bool = False,
-        interpolation: int = cv2.INTER_LINEAR,
-        mask_interpolation: int = cv2.INTER_NEAREST,
-        border_mode: int = cv2.BORDER_CONSTANT,
-        fill: ColorType = 0,
-        fill_mask: ColorType = 0,
-        p: float = 0.5,
-        always_apply: bool | None = None,
-    ):
-        super().__init__(p, always_apply=always_apply)
-        self.scale = cast(tuple[float, float], scale)
-        self.keep_size = keep_size
-        self.border_mode = border_mode
-        self.fill = fill
-        self.fill_mask = fill_mask
-        self.fit_output = fit_output
-        self.interpolation = interpolation
-        self.mask_interpolation = mask_interpolation
-
-    def apply(
-        self,
-        img: np.ndarray,
-        matrix: np.ndarray,
-        max_height: int,
-        max_width: int,
-        **params: Any,
-    ) -> np.ndarray:
-        return fgeometric.perspective(
-            img,
-            matrix,
-            max_width,
-            max_height,
-            self.fill,
-            self.border_mode,
-            self.keep_size,
-            self.interpolation,
-        )
-
-    def apply_to_mask(
-        self,
-        mask: np.ndarray,
-        matrix: np.ndarray,
-        max_height: int,
-        max_width: int,
-        **params: Any,
-    ) -> np.ndarray:
-        return fgeometric.perspective(
-            mask,
-            matrix,
-            max_width,
-            max_height,
-            self.fill_mask,
-            self.border_mode,
-            self.keep_size,
-            self.mask_interpolation,
-        )
-
-    def apply_to_bboxes(
-        self,
-        bboxes: np.ndarray,
-        matrix_bbox: np.ndarray,
-        max_height: int,
-        max_width: int,
-        **params: Any,
-    ) -> np.ndarray:
-        return fgeometric.perspective_bboxes(
-            bboxes,
-            params["shape"],
-            matrix_bbox,
-            max_width,
-            max_height,
-            self.keep_size,
-        )
-
-    def apply_to_keypoints(
-        self,
-        keypoints: np.ndarray,
-        matrix: np.ndarray,
-        max_height: int,
-        max_width: int,
-        **params: Any,
-    ) -> np.ndarray:
-        return fgeometric.perspective_keypoints(
-            keypoints,
-            params["shape"],
-            matrix,
-            max_width,
-            max_height,
-            self.keep_size,
-        )
-
-    def get_params_dependent_on_data(
-        self,
-        params: dict[str, Any],
-        data: dict[str, Any],
-    ) -> dict[str, Any]:
-        image_shape = params["shape"][:2]
-
-        scale = self.py_random.uniform(*self.scale)
-
-        points = fgeometric.generate_perspective_points(
-            image_shape,
-            scale,
-            self.random_generator,
-        )
-        points = fgeometric.order_points(points)
-
-        matrix, max_width, max_height = fgeometric.compute_perspective_params(
-            points,
-            image_shape,
-        )
-
-        if self.fit_output:
-            matrix, max_width, max_height = fgeometric.expand_transform(
-                matrix,
-                image_shape,
-            )
-
-        return {
-            "matrix": matrix,
-            "max_height": max_height,
-            "max_width": max_width,
-            "matrix_bbox": matrix,
-        }
-
-    def get_transform_init_args_names(self) -> tuple[str, ...]:
-        return (
-            "scale",
-            "keep_size",
-            "border_mode",
-            "fill",
-            "fill_mask",
-            "fit_output",
-            "interpolation",
-            "mask_interpolation",
-        )
+        pad_mode: BorderModeType | None
+        pad_val: ColorType | None
+        mask_pad_val: ColorType | None
+        fit_output: bool
+        interpolation: InterpolationType
+        mask_interpolation: InterpolationType
+        fill: ColorType
+        fill_mask: ColorType
+        border_mode: BorderModeType
+
+        @model_validator(mode="after")
+        def validate_deprecated_fields(self) -> Self:
+            if self.pad_mode is not None:
+                warn("pad_mode is deprecated, use border_mode instead", DeprecationWarning, stacklevel=2)
+                self.border_mode = self.pad_mode
+            if self.pad_val is not None:
+                warn("pad_val is deprecated, use fill instead", DeprecationWarning, stacklevel=2)
+                self.fill = self.pad_val
+            if self.mask_pad_val is not None:
+                warn("mask_pad_val is deprecated, use fill_mask instead", DeprecationWarning, stacklevel=2)
+                self.fill_mask = self.mask_pad_val
+            return self
+
+    def __init__(
+        self,
+        scale: ScaleFloatType = (0.05, 0.1),
+        keep_size: bool = True,
+        pad_mode: int | None = None,
+        pad_val: ColorType | None = None,
+        mask_pad_val: ColorType | None = None,
+        fit_output: bool = False,
+        interpolation: int = cv2.INTER_LINEAR,
+        mask_interpolation: int = cv2.INTER_NEAREST,
+        border_mode: int = cv2.BORDER_CONSTANT,
+        fill: ColorType = 0,
+        fill_mask: ColorType = 0,
+        p: float = 0.5,
+        always_apply: bool | None = None,
+    ):
+        super().__init__(p, always_apply=always_apply)
+        self.scale = cast(tuple[float, float], scale)
+        self.keep_size = keep_size
+        self.border_mode = border_mode
+        self.fill = fill
+        self.fill_mask = fill_mask
+        self.fit_output = fit_output
+        self.interpolation = interpolation
+        self.mask_interpolation = mask_interpolation
+
+    def apply(
+        self,
+        img: np.ndarray,
+        matrix: np.ndarray,
+        max_height: int,
+        max_width: int,
+        **params: Any,
+    ) -> np.ndarray:
+        return fgeometric.perspective(
+            img,
+            matrix,
+            max_width,
+            max_height,
+            self.fill,
+            self.border_mode,
+            self.keep_size,
+            self.interpolation,
+        )
+
+    def apply_to_mask(
+        self,
+        mask: np.ndarray,
+        matrix: np.ndarray,
+        max_height: int,
+        max_width: int,
+        **params: Any,
+    ) -> np.ndarray:
+        return fgeometric.perspective(
+            mask,
+            matrix,
+            max_width,
+            max_height,
+            self.fill_mask,
+            self.border_mode,
+            self.keep_size,
+            self.mask_interpolation,
+        )
+
+    def apply_to_bboxes(
+        self,
+        bboxes: np.ndarray,
+        matrix_bbox: np.ndarray,
+        max_height: int,
+        max_width: int,
+        **params: Any,
+    ) -> np.ndarray:
+        return fgeometric.perspective_bboxes(
+            bboxes,
+            params["shape"],
+            matrix_bbox,
+            max_width,
+            max_height,
+            self.keep_size,
+        )
+
+    def apply_to_keypoints(
+        self,
+        keypoints: np.ndarray,
+        matrix: np.ndarray,
+        max_height: int,
+        max_width: int,
+        **params: Any,
+    ) -> np.ndarray:
+        return fgeometric.perspective_keypoints(
+            keypoints,
+            params["shape"],
+            matrix,
+            max_width,
+            max_height,
+            self.keep_size,
+        )
+
+    def get_params_dependent_on_data(
+        self,
+        params: dict[str, Any],
+        data: dict[str, Any],
+    ) -> dict[str, Any]:
+        image_shape = params["shape"][:2]
+
+        scale = self.py_random.uniform(*self.scale)
+
+        points = fgeometric.generate_perspective_points(
+            image_shape,
+            scale,
+            self.random_generator,
+        )
+        points = fgeometric.order_points(points)
+
+        matrix, max_width, max_height = fgeometric.compute_perspective_params(
+            points,
+            image_shape,
+        )
+
+        if self.fit_output:
+            matrix, max_width, max_height = fgeometric.expand_transform(
+                matrix,
+                image_shape,
+            )
+
+        return {
+            "matrix": matrix,
+            "max_height": max_height,
+            "max_width": max_width,
+            "matrix_bbox": matrix,
+        }
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return (
+            "scale",
+            "keep_size",
+            "border_mode",
+            "fill",
+            "fill_mask",
+            "fit_output",
+            "interpolation",
+            "mask_interpolation",
+        )
 

class PiecewiseAffine (scale=(0.03, 0.05), nb_rows=(4, 4), nb_cols=(4, 4), interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, absolute_scale=False, p=0.5, always_apply=None, keypoints_threshold=0.01) [view source on GitHub] ¶

Apply piecewise affine transformations to the input image.

This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points around via affine transformations. This leads to local distortions in the image.

Parameters:

Name Type Description
scale tuple[float, float] | float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Recommended values are in the range (0.01, 0.05) for small distortions, and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).

nb_rows tuple[int, int] | int

Number of rows of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of rows. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

nb_cols tuple[int, int] | int

Number of columns of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of columns. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

absolute_scale bool

If set to True, the value of the scale parameter will be treated as an absolute pixel value. If set to False, it will be treated as a fraction of the image height and width. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This augmentation is very slow. Consider using ElasticTransform instead, which is at least 10x faster.
  • The augmentation may not always produce visible effects, especially with small scale values.
  • For keypoints and bounding boxes, the transformation might move them outside the image boundaries. In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
@@ -2209,41 +2208,41 @@
     _targets = ALL_TARGETS
 
     class InitSchema(BaseTransformInitSchema):
-        shift_limit: SymmetricRangeType = (-0.0625, 0.0625)
-        scale_limit: SymmetricRangeType = (-0.1, 0.1)
-        rotate_limit: SymmetricRangeType = (-45, 45)
-        interpolation: InterpolationType = cv2.INTER_LINEAR
-        border_mode: BorderModeType = cv2.BORDER_REFLECT_101
+        shift_limit: SymmetricRangeType
+        scale_limit: SymmetricRangeType
+        rotate_limit: SymmetricRangeType
+        interpolation: InterpolationType
+        border_mode: BorderModeType
 
-        value: ColorType | None = Field(
-            default=None,
-            deprecated="Deprecated. Use fill instead.",
-        )
-        mask_value: ColorType | None = Field(
-            default=None,
-            deprecated="Deprecated. Use fill_mask instead.",
-        )
-
-        fill: ColorType = 0
-        fill_mask: ColorType = 0
-
-        shift_limit_x: ScaleFloatType | None = Field(default=None)
-        shift_limit_y: ScaleFloatType | None = Field(default=None)
-        rotate_method: Literal["largest_box", "ellipse"] = "largest_box"
-        mask_interpolation: InterpolationType
-
-        @model_validator(mode="after")
-        def check_shift_limit(self) -> Self:
-            bounds = -1, 1
-            self.shift_limit_x = to_tuple(
-                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,
-            )
-            check_range(self.shift_limit_x, *bounds, "shift_limit_x")
-            self.shift_limit_y = to_tuple(
-                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,
-            )
-            check_range(self.shift_limit_y, *bounds, "shift_limit_y")
-
+        value: ColorType | None
+        mask_value: ColorType | None
+
+        fill: ColorType = 0
+        fill_mask: ColorType = 0
+
+        shift_limit_x: ScaleFloatType | None
+        shift_limit_y: ScaleFloatType | None
+        rotate_method: Literal["largest_box", "ellipse"]
+        mask_interpolation: InterpolationType
+
+        @model_validator(mode="after")
+        def check_shift_limit(self) -> Self:
+            bounds = -1, 1
+            self.shift_limit_x = to_tuple(
+                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,
+            )
+            check_range(self.shift_limit_x, *bounds, "shift_limit_x")
+            self.shift_limit_y = to_tuple(
+                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,
+            )
+            check_range(self.shift_limit_y, *bounds, "shift_limit_y")
+
+            if self.value is not None:
+                warn("value is deprecated, use fill instead", DeprecationWarning, stacklevel=2)
+                self.fill = self.value
+            if self.mask_value is not None:
+                warn("mask_value is deprecated, use fill_mask instead", DeprecationWarning, stacklevel=2)
+                self.fill_mask = self.mask_value
             return self
 
         @field_validator("scale_limit")
@@ -2489,7 +2488,7 @@
             "num_control_points",
             *super().get_transform_init_args_names(),
         )
-

class Transpose [view source on GitHub] ¶

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np
+

class Transpose [view source on GitHub] ¶

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.array([
 ...     [[1, 2, 3], [4, 5, 6]],
@@ -2567,7 +2566,7 @@
 
     def get_transform_init_args_names(self) -> tuple[()]:
         return ()
-

class VerticalFlip [view source on GitHub] ¶

Flip the input vertically around the x-axis.

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np
+

class VerticalFlip [view source on GitHub] ¶

Flip the input vertically around the x-axis.

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> image = np.array([
 ...     [[1, 2, 3], [4, 5, 6]],
diff --git a/docs/api_reference/augmentations/transforms3d/functional/index.html b/docs/api_reference/augmentations/transforms3d/functional/index.html
index 286e1146..f98d18e3 100644
--- a/docs/api_reference/augmentations/transforms3d/functional/index.html
+++ b/docs/api_reference/augmentations/transforms3d/functional/index.html
@@ -6,7 +6,7 @@
   .jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon {
     font-size: 0.8rem;
   }
-    

3D (Volumetric) functional transforms (augmentations.transforms3d.functional)

def adjust_padding_by_position3d (paddings, position, py_random) [view source on GitHub]¶

Adjust padding values based on desired position for 3D data.

Parameters:

Name Type Description
paddings list[tuple[int, int]]

List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]

position Literal['center', 'random']

Position of the image after padding. Either 'center' or 'random'

py_random Random

Random number generator

Returns:

Type Description
tuple[int, int, int, int, int, int]

Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def adjust_padding_by_position3d(
+    

3D (Volumetric) functional transforms (augmentations.transforms3d.functional)

def adjust_padding_by_position3d (paddings, position, py_random) [view source on GitHub]¶

Adjust padding values based on desired position for 3D data.

Parameters:

Name Type Description
paddings list[tuple[int, int]]

List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]

position Literal['center', 'random']

Position of the image after padding. Either 'center' or 'random'

py_random Random

Random number generator

Returns:

Type Description
tuple[int, int, int, int, int, int]

Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def adjust_padding_by_position3d(
     paddings: list[tuple[int, int]],  # [(front, back), (top, bottom), (left, right)]
     position: Literal["center", "random"],
     py_random: random.Random,
@@ -44,7 +44,7 @@
         py_random.randint(0, w_pad),  # w_left
         w_pad - py_random.randint(0, w_pad),  # w_right
     )
-

def crop3d (volume, crop_coords) [view source on GitHub]¶

Crop 3D volume using coordinates.

Parameters:

Name Type Description
volume ndarray

Input volume with shape (z, y, x) or (z, y, x, channels)

crop_coords tuple[int, int, int, int, int, int]

Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping

Returns:

Type Description
ndarray

Cropped volume with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def crop3d(
+

def crop3d (volume, crop_coords) [view source on GitHub]¶

Crop 3D volume using coordinates.

Parameters:

Name Type Description
volume ndarray

Input volume with shape (z, y, x) or (z, y, x, channels)

crop_coords tuple[int, int, int, int, int, int]

Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping

Returns:

Type Description
ndarray

Cropped volume with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def crop3d(
     volume: np.ndarray,
     crop_coords: tuple[int, int, int, int, int, int],
 ) -> np.ndarray:
@@ -60,51 +60,110 @@
     z_min, z_max, y_min, y_max, x_min, x_max = crop_coords
 
     return volume[z_min:z_max, y_min:y_max, x_min:x_max]
-

def cutout3d (volume, holes, fill_value) [view source on GitHub]¶

Cut out holes in 3D volume and fill them with a given value.

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def cutout3d(volume: np.ndarray, holes: np.ndarray, fill_value: ColorType) -> np.ndarray:
+

def cutout3d (volume, holes, fill_value) [view source on GitHub]¶

Cut out holes in 3D volume and fill them with a given value.

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def cutout3d(volume: np.ndarray, holes: np.ndarray, fill_value: ColorType) -> np.ndarray:
     """Cut out holes in 3D volume and fill them with a given value."""
     volume = volume.copy()
     for z1, y1, x1, z2, y2, x2 in holes:
         volume[z1:z2, y1:y2, x1:x2] = fill_value
     return volume
-

def pad_3d_with_params (volume, padding, value) [view source on GitHub]¶

Pad 3D image with given parameters.

Parameters:

Name Type Description
volume ndarray

Input volume with shape (depth, height, width) or (depth, height, width, channels)

padding tuple[int, int, int, int, int, int]

Padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

value Union[float, collections.abc.Sequence[float]]

Padding value

Returns:

Type Description
ndarray

Padded image with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def pad_3d_with_params(
+

def filter_keypoints_in_holes3d (keypoints, holes) [view source on GitHub]¶

Filter out keypoints that are inside any of the 3D holes.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (num_keypoints, 3+). The first three columns are x, y, z coordinates.

holes np.ndarray

Array of holes with shape (num_holes, 6). Each hole is represented as [z1, y1, x1, z2, y2, x2].

Returns:

Type Description
np.ndarray

Array of keypoints that are not inside any hole.

Source code in albumentations/augmentations/transforms3d/functional.py
Python
@handle_empty_array("keypoints")
+def filter_keypoints_in_holes3d(keypoints: np.ndarray, holes: np.ndarray) -> np.ndarray:
+    """Filter out keypoints that are inside any of the 3D holes.
+
+    Args:
+        keypoints (np.ndarray): Array of keypoints with shape (num_keypoints, 3+).
+                               The first three columns are x, y, z coordinates.
+        holes (np.ndarray): Array of holes with shape (num_holes, 6).
+                           Each hole is represented as [z1, y1, x1, z2, y2, x2].
+
+    Returns:
+        np.ndarray: Array of keypoints that are not inside any hole.
+    """
+    if holes.size == 0:
+        return keypoints
+
+    # Broadcast keypoints and holes for vectorized comparison
+    # Convert keypoints from XYZ to ZYX for comparison with holes
+    kp_z = keypoints[:, 2][:, np.newaxis]  # Shape: (num_keypoints, 1)
+    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)
+    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)
+
+    # Extract hole coordinates (in ZYX order)
+    hole_z1 = holes[:, 0]  # Shape: (num_holes,)
+    hole_y1 = holes[:, 1]
+    hole_x1 = holes[:, 2]
+    hole_z2 = holes[:, 3]
+    hole_y2 = holes[:, 4]
+    hole_x2 = holes[:, 5]
+
+    # Check if each keypoint is inside each hole
+    inside_hole = (
+        (kp_z >= hole_z1)
+        & (kp_z < hole_z2)
+        & (kp_y >= hole_y1)
+        & (kp_y < hole_y2)
+        & (kp_x >= hole_x1)
+        & (kp_x < hole_x2)
+    )
+
+    # A keypoint is valid if it's not inside any hole
+    valid_keypoints = ~np.any(inside_hole, axis=1)
+
+    # Return filtered keypoints with same dtype as input
+    result = keypoints[valid_keypoints]
+    if len(result) == 0:
+        # Ensure empty result has correct shape and dtype
+        return np.array([], dtype=keypoints.dtype).reshape(0, keypoints.shape[1])
+    return result
+

def pad_3d_with_params (volume, padding, value) [view source on GitHub]¶

Pad 3D volume with given parameters.

Parameters:

Name Type Description
volume ndarray

Input volume with shape (depth, height, width) or (depth, height, width, channels)

padding tuple[int, int, int, int, int, int]

Padding values in format: (depth_front, depth_back, height_top, height_bottom, width_left, width_right) where: - depth_front/back: padding at start/end of depth axis (z) - height_top/bottom: padding at start/end of height axis (y) - width_left/right: padding at start/end of width axis (x)

value Union[float, collections.abc.Sequence[float]]

Value to fill the padding

Returns:

Type Description
ndarray

Padded volume with same number of dimensions as input

Note

The padding order matches the volume dimensions (depth, height, width). For each dimension, the first value is padding at the start (smaller indices), and the second value is padding at the end (larger indices).

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def pad_3d_with_params(
     volume: np.ndarray,
-    padding: tuple[int, int, int, int, int, int],  # (d_front, d_back, h_top, h_bottom, w_left, w_right)
+    padding: tuple[int, int, int, int, int, int],
     value: ColorType,
 ) -> np.ndarray:
-    """Pad 3D image with given parameters.
+    """Pad 3D volume with given parameters.
 
     Args:
         volume: Input volume with shape (depth, height, width) or (depth, height, width, channels)
-        padding: Padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)
-        value: Padding value
-
-    Returns:
-        Padded image with same number of dimensions as input
-    """
-    d_front, d_back, h_top, h_bottom, w_left, w_right = padding
+        padding: Padding values in format:
+            (depth_front, depth_back, height_top, height_bottom, width_left, width_right)
+            where:
+            - depth_front/back: padding at start/end of depth axis (z)
+            - height_top/bottom: padding at start/end of height axis (y)
+            - width_left/right: padding at start/end of width axis (x)
+        value: Value to fill the padding
 
-    # Skip if no padding is needed
-    if d_front == d_back == h_top == h_bottom == w_left == w_right == 0:
-        return volume
-
-    # Handle both 3D and 4D arrays
-    pad_width = [
-        (d_front, d_back),  # depth padding
-        (h_top, h_bottom),  # height padding
-        (w_left, w_right),  # width padding
-    ]
-
-    # Add channel padding if 4D array
-    if volume.ndim == NUM_VOLUME_DIMENSIONS:
-        pad_width.append((0, 0))  # no padding for channels
-
-    return np.pad(
-        volume,
-        pad_width=pad_width,
-        mode="constant",
-        constant_values=value,
-    )
-

def transform_cube (cube, index) [view source on GitHub]¶

Transform cube by index (0-47)

Parameters:

Name Type Description
cube ndarray

Input array with shape (D, H, W) or (D, H, W, C)

index int

Integer from 0 to 47 specifying which transformation to apply

Returns:

Type Description
ndarray

Transformed cube with same shape as input

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def transform_cube(cube: np.ndarray, index: int) -> np.ndarray:
+    Returns:
+        Padded volume with same number of dimensions as input
+
+    Note:
+        The padding order matches the volume dimensions (depth, height, width).
+        For each dimension, the first value is padding at the start (smaller indices),
+        and the second value is padding at the end (larger indices).
+    """
+    depth_front, depth_back, height_top, height_bottom, width_left, width_right = padding
+
+    # Skip if no padding is needed
+    if all(p == 0 for p in padding):
+        return volume
+
+    # Handle both 3D and 4D arrays
+    pad_width = [
+        (depth_front, depth_back),  # depth (z) padding
+        (height_top, height_bottom),  # height (y) padding
+        (width_left, width_right),  # width (x) padding
+    ]
+
+    # Add channel padding if 4D array
+    if volume.ndim == NUM_VOLUME_DIMENSIONS:
+        pad_width.append((0, 0))  # no padding for channels
+
+    return np.pad(
+        volume,
+        pad_width=pad_width,
+        mode="constant",
+        constant_values=value,
+    )
+

def transform_cube (cube, index) [view source on GitHub]¶

Transform cube by index (0-47)

Parameters:

Name Type Description
cube ndarray

Input array with shape (D, H, W) or (D, H, W, C)

index int

Integer from 0 to 47 specifying which transformation to apply

Returns:

Type Description
ndarray

Transformed cube with same shape as input

Source code in albumentations/augmentations/transforms3d/functional.py
Python
def transform_cube(cube: np.ndarray, index: int) -> np.ndarray:
     """Transform cube by index (0-47)
 
     Args:
diff --git a/docs/api_reference/augmentations/transforms3d/transforms/index.html b/docs/api_reference/augmentations/transforms3d/transforms/index.html
index 8d935c19..c1666bae 100644
--- a/docs/api_reference/augmentations/transforms3d/transforms/index.html
+++ b/docs/api_reference/augmentations/transforms3d/transforms/index.html
@@ -9,7 +9,7 @@
     

3D (Volumetric) transforms (augmentations.transforms3d.transforms)

class BaseCropAndPad3D (pad_if_needed, fill, fill_mask, pad_position, p=1.0, always_apply=None) [view source on GitHub] ¶

Base class for 3D transforms that need both cropping and padding.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class BaseCropAndPad3D(Transform3D):
     """Base class for 3D transforms that need both cropping and padding."""
 
-    _targets = (Targets.VOLUME, Targets.MASK3D)
+    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)
 
     class InitSchema(Transform3D.InitSchema):
         pad_if_needed: bool
@@ -142,10 +142,42 @@
             )
 
         return cropped
+
+    def apply_to_keypoints(
+        self,
+        keypoints: np.ndarray,
+        crop_coords: tuple[int, int, int, int, int, int],
+        pad_params: dict[str, int] | None,
+        **params: Any,
+    ) -> np.ndarray:
+        # Extract crop start coordinates (z1,y1,x1)
+        crop_z1, _, crop_y1, _, crop_x1, _ = crop_coords
+
+        # Initialize shift vector with negative crop coordinates
+        shift = np.array(
+            [
+                -crop_x1,  # X shift
+                -crop_y1,  # Y shift
+                -crop_z1,  # Z shift
+            ],
+        )
+
+        # Add padding shift if needed
+        if pad_params is not None:
+            shift += np.array(
+                [
+                    pad_params["pad_left"],  # X shift
+                    pad_params["pad_top"],  # Y shift
+                    pad_params["pad_front"],  # Z shift
+                ],
+            )
+
+        # Apply combined shift
+        return fgeometric.shift_keypoints(keypoints, shift)
 

class BasePad3D (fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Base class for 3D padding transforms.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class BasePad3D(Transform3D):
     """Base class for 3D padding transforms."""
 
-    _targets = (Targets.VOLUME, Targets.MASK3D)
+    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)
 
     class InitSchema(Transform3D.InitSchema):
         fill: ColorType
@@ -189,7 +221,12 @@
             padding=padding,
             value=cast(ColorType, self.fill_mask),
         )
-

class CenterCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Crop the center of 3D volume.

Parameters:

Name Type Description
size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

If you want to perform cropping only in the XY plane while preserving all slices along the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class CenterCrop3D(BaseCropAndPad3D):
+
+    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:
+        padding = params["padding"]
+        shift_vector = np.array([padding[4], padding[2], padding[0]])
+        return fgeometric.shift_keypoints(keypoints, shift_vector)
+

class CenterCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Crop the center of 3D volume.

Parameters:

Name Type Description
size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

If you want to perform cropping only in the XY plane while preserving all slices along the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class CenterCrop3D(BaseCropAndPad3D):
     """Crop the center of 3D volume.
 
     Args:
@@ -200,7 +237,7 @@
         p (float): probability of applying the transform. Default: 1.0
 
     Targets:
-        volume, mask3d
+        volume, mask3d, keypoints
 
     Image types:
         uint8, float32
@@ -285,7 +322,7 @@
 
     def get_transform_init_args_names(self) -> tuple[str, ...]:
         return "size", "pad_if_needed", "fill", "fill_mask"
-

class CoarseDropout3D (num_holes_range=(1, 1), hole_depth_range=(0.1, 0.2), hole_height_range=(0.1, 0.2), hole_width_range=(0.1, 0.2), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub] ¶

CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally, the corresponding regions in an associated 3D mask, to simulate occlusion and varied object sizes found in real-world volumetric data.

Parameters:

Name Type Description
num_holes_range tuple[int, int]

Range (min, max) for the number of cuboid regions to drop out. Default: (1, 1)

hole_depth_range tuple[float, float]

Range (min, max) for the depth of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)

hole_height_range tuple[float, float]

Range (min, max) for the height of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)

hole_width_range tuple[float, float]

Range (min, max) for the width of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)

fill ColorType

Value for the dropped voxels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the 3D mask. If None, mask regions corresponding to volume dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

volume, mask3d

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges.
  • All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.
  • If you want to apply dropout only in the XY plane while preserving the full depth dimension, consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout to each slice independently, effectively creating cylindrical dropout regions that extend through the entire depth of the volume.

Examples:

Python
>>> import numpy as np
+

class CoarseDropout3D (num_holes_range=(1, 1), hole_depth_range=(0.1, 0.2), hole_height_range=(0.1, 0.2), hole_width_range=(0.1, 0.2), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub] ¶

CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally, the corresponding regions in an associated 3D mask, to simulate occlusion and varied object sizes found in real-world volumetric data.

Parameters:

Name Type Description
num_holes_range tuple[int, int]

Range (min, max) for the number of cuboid regions to drop out. Default: (1, 1)

hole_depth_range tuple[float, float]

Range (min, max) for the depth of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)

hole_height_range tuple[float, float]

Range (min, max) for the height of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)

hole_width_range tuple[float, float]

Range (min, max) for the width of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)

fill ColorType

Value for the dropped voxels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the 3D mask. If None, mask regions corresponding to volume dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges.
  • All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.
  • If you want to apply dropout only in the XY plane while preserving the full depth dimension, consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout to each slice independently, effectively creating cylindrical dropout regions that extend through the entire depth of the volume.

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)
 >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)
@@ -322,7 +359,7 @@
         p (float): Probability of applying the transform. Default: 0.5
 
     Targets:
-        volume, mask3d
+        volume, mask3d, keypoints
 
     Image types:
         uint8, float32
@@ -352,7 +389,7 @@
         >>> transformed_volume, transformed_mask3d = transformed["volume"], transformed["mask3d"]
     """
 
-    _targets = (Targets.VOLUME, Targets.MASK3D)
+    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)
 
     class InitSchema(Transform3D.InitSchema):
         num_holes_range: Annotated[
@@ -469,16 +506,31 @@
 
         return f3d.cutout3d(mask, holes, cast(ColorType, self.fill_mask))
 
-    def get_transform_init_args_names(self) -> tuple[str, ...]:
-        return (
-            "num_holes_range",
-            "hole_depth_range",
-            "hole_height_range",
-            "hole_width_range",
-            "fill",
-            "fill_mask",
-        )
-

class CubicSymmetry (p=1.0, always_apply=None) [view source on GitHub] ¶

Applies a random cubic symmetry transformation to a 3D volume.

This transform is a 3D extension of D4. While D4 handles the 8 symmetries of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube. Like D4, this transform does not create any interpolation artifacts as it only remaps voxels from one position to another without any interpolation.

The 48 transformations consist of: - 24 rotations (orientation-preserving): * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24) - 24 rotoreflections (orientation-reversing): * Reflection through a plane followed by any of the 24 rotations

For a cube, these transformations preserve: - All face centers (6) - All vertex positions (8) - All edge centers (12)

works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for data augmentation in 3D medical imaging, crystallography, and voxel-based 3D modeling where the object's orientation is arbitrary.
  • All transformations preserve the object's chirality (handedness) when using pure rotations (indices 0-23) and invert it when using rotoreflections (indices 24-47).

Examples:

Python
>>> import numpy as np
+    def apply_to_keypoints(
+        self,
+        keypoints: np.ndarray,
+        holes: np.ndarray,
+        **params: Any,
+    ) -> np.ndarray:
+        """Remove keypoints that fall within dropout regions."""
+        if holes.size == 0:
+            return keypoints
+        processor = cast(KeypointsProcessor, self.get_processor("keypoints"))
+
+        if processor is None or not processor.params.remove_invisible:
+            return keypoints
+        return f3d.filter_keypoints_in_holes3d(keypoints, holes)
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return (
+            "num_holes_range",
+            "hole_depth_range",
+            "hole_height_range",
+            "hole_width_range",
+            "fill",
+            "fill_mask",
+        )
+

class CubicSymmetry (p=1.0, always_apply=None) [view source on GitHub] ¶

Applies a random cubic symmetry transformation to a 3D volume.

This transform is a 3D extension of D4. While D4 handles the 8 symmetries of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube. Like D4, this transform does not create any interpolation artifacts as it only remaps voxels from one position to another without any interpolation.

The 48 transformations consist of: - 24 rotations (orientation-preserving): * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24) - 24 rotoreflections (orientation-reversing): * Reflection through a plane followed by any of the 24 rotations

For a cube, these transformations preserve: - All face centers (6) - All vertex positions (8) - All edge centers (12)

works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)

Parameters:

Name Type Description
p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

  • This transform is particularly useful for data augmentation in 3D medical imaging, crystallography, and voxel-based 3D modeling where the object's orientation is arbitrary.
  • All transformations preserve the object's chirality (handedness) when using pure rotations (indices 0-23) and invert it when using rotoreflections (indices 24-47).

Examples:

Python
>>> import numpy as np
 >>> import albumentations as A
 >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)
 >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)
@@ -511,7 +563,7 @@
         p (float): Probability of applying the transform. Default: 1.0
 
     Targets:
-        volume, mask3d
+        volume, mask3d, keypoints
 
     Image types:
         uint8, float32
@@ -538,7 +590,7 @@
         - D4: The 2D version that handles the 8 symmetries of a square
     """
 
-    _targets = (Targets.VOLUME, Targets.MASK3D)
+    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)
 
     def __init__(
         self,
@@ -553,87 +605,88 @@
         data: dict[str, Any],
     ) -> dict[str, Any]:
         # Randomly select one of 48 possible transformations
-        return {"index": self.py_random.randint(0, 47)}
-
-    def apply_to_volume(self, volume: np.ndarray, index: int, **params: Any) -> np.ndarray:
-        return f3d.transform_cube(volume, index)
-
-    def get_transform_init_args_names(self) -> tuple[str, ...]:
-        return ()
-

class Pad3D (padding, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Pad the sides of a 3D volume by specified number of voxels.

Parameters:

Name Type Description
padding int, tuple[int, int, int] or tuple[int, int, int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int, int] - symmetric padding (pad_z, pad_y, pad_x) where: - pad_z: padding for depth/z-axis (front/back) - pad_y: padding for height/y-axis (top/bottom) - pad_x: padding for width/x-axis (left/right) * tuple[int, int, int, int, int, int] - explicit padding per side in order: (front, top, left, back, bottom, right) where: - front/back: padding along z-axis (depth) - top/bottom: padding along y-axis (height) - left/right: padding along x-axis (width)

fill ColorType

Padding value for image

fill_mask ColorType

Padding value for mask

p float

probability of applying the transform. Default: 1.0.

Targets

volume, mask3d

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class Pad3D(BasePad3D):
+
+        volume_shape = data["volume"].shape
+        return {"index": self.py_random.randint(0, 47), "volume_shape": volume_shape}
+
+    def apply_to_volume(self, volume: np.ndarray, index: int, **params: Any) -> np.ndarray:
+        return f3d.transform_cube(volume, index)
+
+    def apply_to_keypoints(self, keypoints: np.ndarray, index: int, **params: Any) -> np.ndarray:
+        return f3d.transform_cube_keypoints(keypoints, index, volume_shape=params["volume_shape"])
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return ()
+

class Pad3D (padding, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Pad the sides of a 3D volume by specified number of voxels.

Parameters:

Name Type Description
padding int, tuple[int, int, int] or tuple[int, int, int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int, int] - symmetric padding (depth, height, width) where each value is applied to both sides of the corresponding dimension * tuple[int, int, int, int, int, int] - explicit padding per side in order: (depth_front, depth_back, height_top, height_bottom, width_left, width_right)

fill ColorType

Padding value for image

fill_mask ColorType

Padding value for mask

p float

probability of applying the transform. Default: 1.0.

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class Pad3D(BasePad3D):
     """Pad the sides of a 3D volume by specified number of voxels.
 
     Args:
         padding (int, tuple[int, int, int] or tuple[int, int, int, int, int, int]): Padding values. Can be:
             * int - pad all sides by this value
-            * tuple[int, int, int] - symmetric padding (pad_z, pad_y, pad_x) where:
-                - pad_z: padding for depth/z-axis (front/back)
-                - pad_y: padding for height/y-axis (top/bottom)
-                - pad_x: padding for width/x-axis (left/right)
-            * tuple[int, int, int, int, int, int] - explicit padding per side in order:
-                (front, top, left, back, bottom, right) where:
-                - front/back: padding along z-axis (depth)
-                - top/bottom: padding along y-axis (height)
-                - left/right: padding along x-axis (width)
-        fill (ColorType): Padding value for image
-        fill_mask (ColorType): Padding value for mask
-        p (float): probability of applying the transform. Default: 1.0.
-
-    Targets:
-        volume, mask3d
-
-    Image types:
-        uint8, float32
-
-    Note:
-        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),
-        with optional channel dimension as the last axis.
-    """
-
-    class InitSchema(BasePad3D.InitSchema):
-        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int]
-
-        @field_validator("padding")
-        @classmethod
-        def validate_padding(
-            cls,
-            v: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],
-        ) -> int | tuple[int, int, int] | tuple[int, int, int, int, int, int]:
-            if isinstance(v, int) and v < 0:
-                raise ValueError("Padding value must be non-negative")
-            if isinstance(v, tuple) and not all(isinstance(i, int) and i >= 0 for i in v):
-                raise ValueError("Padding tuple must contain non-negative integers")
-
-            return v
-
-    def __init__(
-        self,
-        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],
-        fill: ColorType = 0,
-        fill_mask: ColorType = 0,
-        p: float = 1.0,
-        always_apply: bool | None = None,
-    ):
-        super().__init__(fill=fill, fill_mask=fill_mask, p=p)
-        self.padding = padding
-        self.fill = fill
-        self.fill_mask = fill_mask
-
-    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:
-        if isinstance(self.padding, int):
-            pad_d = pad_h = pad_w = self.padding
-            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)
-        elif len(self.padding) == NUM_DIMENSIONS:
-            pad_d, pad_h, pad_w = self.padding  # type: ignore[misc]
-            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)
-        else:
-            padding = self.padding  # type: ignore[assignment]
-
-        return {"padding": padding}
-
-    def get_transform_init_args_names(self) -> tuple[str, ...]:
-        return "padding", "fill", "fill_mask"
-

class PadIfNeeded3D (min_zyx=None, pad_divisor_zyx=None, position='center', fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions. If the pad_divisor_zyx is specified, the function additionally ensures that the volume dimensions are divisible by these values.

Parameters:

Name Type Description
min_zyx tuple[int, int, int] | None

Minimum desired size as (depth, height, width). Ensures volume dimensions are at least these values. If not specified, pad_divisor_zyx must be provided.

pad_divisor_zyx tuple[int, int, int] | None

If set, pads each dimension to make it divisible by corresponding value in format (depth_div, height_div, width_div). If not specified, min_zyx must be provided.

position Literal["center", "random"]

Position where the volume is to be placed after padding. Default is 'center'.

fill ColorType

Value to fill the border voxels for volume. Default: 0

fill_mask ColorType

Value to fill the border voxels for masks. Default: 0

p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class PadIfNeeded3D(BasePad3D):
+            * tuple[int, int, int] - symmetric padding (depth, height, width) where each value
+              is applied to both sides of the corresponding dimension
+            * tuple[int, int, int, int, int, int] - explicit padding per side in order:
+              (depth_front, depth_back, height_top, height_bottom, width_left, width_right)
+
+        fill (ColorType): Padding value for image
+        fill_mask (ColorType): Padding value for mask
+        p (float): probability of applying the transform. Default: 1.0.
+
+    Targets:
+        volume, mask3d, keypoints
+
+    Image types:
+        uint8, float32
+
+    Note:
+        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),
+        with optional channel dimension as the last axis.
+    """
+
+    class InitSchema(BasePad3D.InitSchema):
+        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int]
+
+        @field_validator("padding")
+        @classmethod
+        def validate_padding(
+            cls,
+            v: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],
+        ) -> int | tuple[int, int, int] | tuple[int, int, int, int, int, int]:
+            if isinstance(v, int) and v < 0:
+                raise ValueError("Padding value must be non-negative")
+            if isinstance(v, tuple) and not all(isinstance(i, int) and i >= 0 for i in v):
+                raise ValueError("Padding tuple must contain non-negative integers")
+
+            return v
+
+    def __init__(
+        self,
+        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],
+        fill: ColorType = 0,
+        fill_mask: ColorType = 0,
+        p: float = 1.0,
+        always_apply: bool | None = None,
+    ):
+        super().__init__(fill=fill, fill_mask=fill_mask, p=p)
+        self.padding = padding
+        self.fill = fill
+        self.fill_mask = fill_mask
+
+    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:
+        if isinstance(self.padding, int):
+            pad_d = pad_h = pad_w = self.padding
+            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)
+        elif len(self.padding) == NUM_DIMENSIONS:
+            pad_d, pad_h, pad_w = self.padding  # type: ignore[misc]
+            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)
+        else:
+            padding = self.padding  # type: ignore[assignment]
+
+        return {"padding": padding}
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return "padding", "fill", "fill_mask"
+

class PadIfNeeded3D (min_zyx=None, pad_divisor_zyx=None, position='center', fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions. If the pad_divisor_zyx is specified, the function additionally ensures that the volume dimensions are divisible by these values.

Parameters:

Name Type Description
min_zyx tuple[int, int, int] | None

Minimum desired size as (depth, height, width). Ensures volume dimensions are at least these values. If not specified, pad_divisor_zyx must be provided.

pad_divisor_zyx tuple[int, int, int] | None

If set, pads each dimension to make it divisible by corresponding value in format (depth_div, height_div, width_div). If not specified, min_zyx must be provided.

position Literal["center", "random"]

Position where the volume is to be placed after padding. Default is 'center'.

fill ColorType

Value to fill the border voxels for volume. Default: 0

fill_mask ColorType

Value to fill the border voxels for masks. Default: 0

p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class PadIfNeeded3D(BasePad3D):
     """Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions.
     If the pad_divisor_zyx is specified, the function additionally ensures that the volume
     dimensions are divisible by these values.
@@ -652,7 +705,7 @@
         p (float): Probability of applying the transform. Default: 1.0
 
     Targets:
-        volume, mask3d
+        volume, mask3d, keypoints
 
     Image types:
         uint8, float32
@@ -722,7 +775,7 @@
             "fill",
             "fill_mask",
         )
-

class RandomCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Crop random part of 3D volume.

Parameters:

Name Type Description
size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

If you want to perform random cropping only in the XY plane while preserving all slices along the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class RandomCrop3D(BaseCropAndPad3D):
+

class RandomCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub] ¶

Crop random part of 3D volume.

Parameters:

Name Type Description
size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

If you want to perform random cropping only in the XY plane while preserving all slices along the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py
Python
class RandomCrop3D(BaseCropAndPad3D):
     """Crop random part of 3D volume.
 
     Args:
@@ -733,7 +786,7 @@
         p (float): probability of applying the transform. Default: 1.0
 
     Targets:
-        volume, mask3d
+        volume, mask3d, keypoints
 
     Image types:
         uint8, float32
diff --git a/docs/api_reference/core/bbox_utils/index.html b/docs/api_reference/core/bbox_utils/index.html
index 020ab807..1e927848 100644
--- a/docs/api_reference/core/bbox_utils/index.html
+++ b/docs/api_reference/core/bbox_utils/index.html
@@ -85,7 +85,7 @@
             f" min_visibility={self.min_visibility}, min_width={self.min_width}, min_height={self.min_height},"
             f" check_each_transform={self.check_each_transform}, clip={self.clip})"
         )
-

def bboxes_from_masks (masks) [view source on GitHub]¶

Create bounding boxes from binary masks (fast version)

Parameters:

Name Type Description
masks np.ndarray

Binary masks of shape (H, W) or (N, H, W) where N is the number of masks, and H, W are the height and width of each mask.

Returns:

Type Description
np.ndarray

An array of bounding boxes with shape (N, 4), where each row is (x_min, y_min, x_max, y_max).

Source code in albumentations/core/bbox_utils.py
Python
def bboxes_from_masks(masks: np.ndarray) -> np.ndarray:
+

def bboxes_from_masks (masks) [view source on GitHub]¶

Create bounding boxes from binary masks (fast version)

Parameters:

Name Type Description
masks np.ndarray

Binary masks of shape (H, W) or (N, H, W) where N is the number of masks, and H, W are the height and width of each mask.

Returns:

Type Description
np.ndarray

An array of bounding boxes with shape (N, 4), where each row is (x_min, y_min, x_max, y_max).

Source code in albumentations/core/bbox_utils.py
Python
def bboxes_from_masks(masks: np.ndarray) -> np.ndarray:
     """Create bounding boxes from binary masks (fast version)
 
     Args:
@@ -114,12 +114,12 @@
             bboxes[i] = [x_min, y_min, x_max + 1, y_max + 1]
 
     return bboxes
-

def calculate_bbox_areas_in_pixels (bboxes, image_shape) [view source on GitHub]¶

Calculate areas for multiple bounding boxes.

This function computes the areas of bounding boxes given their normalized coordinates and the dimensions of the image they belong to. The bounding boxes are expected to be in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of shape (N, 4+) where N is the number of bounding boxes. Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates. Additional columns beyond the first 4 are ignored.

image_shape tuple[int, int]

A tuple containing the height and width of the image (height, width).

Returns:

Type Description
np.ndarray

A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels. Returns an empty array if the input bboxes is empty.

Note

  • The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min). Invalid bounding boxes may result in negative areas.
  • The function preserves the input array and creates a copy for internal calculations.
  • The returned areas are in pixel units, not normalized.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])
+

def calculate_bbox_areas_in_pixels (bboxes, shape) [view source on GitHub]¶

Calculate areas for multiple bounding boxes.

This function computes the areas of bounding boxes given their normalized coordinates and the dimensions of the image they belong to. The bounding boxes are expected to be in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of shape (N, 4+) where N is the number of bounding boxes. Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates. Additional columns beyond the first 4 are ignored.

shape ShapeType

A tuple containing the height and width of the image (height, width).

Returns:

Type Description
np.ndarray

A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels. Returns an empty array if the input bboxes is empty.

Note

  • The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min). Invalid bounding boxes may result in negative areas.
  • The function preserves the input array and creates a copy for internal calculations.
  • The returned areas are in pixel units, not normalized.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])
 >>> image_shape = (100, 100)
 >>> areas = calculate_bbox_areas(bboxes, image_shape)
 >>> print(areas)
 [1600. 3600.]
-
Source code in albumentations/core/bbox_utils.py
Python
def calculate_bbox_areas_in_pixels(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:
+
Source code in albumentations/core/bbox_utils.py
Python
def calculate_bbox_areas_in_pixels(bboxes: np.ndarray, shape: ShapeType) -> np.ndarray:
     """Calculate areas for multiple bounding boxes.
 
     This function computes the areas of bounding boxes given their normalized coordinates
@@ -130,7 +130,7 @@
         bboxes (np.ndarray): A numpy array of shape (N, 4+) where N is the number of bounding boxes.
                              Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates.
                              Additional columns beyond the first 4 are ignored.
-        image_shape (tuple[int, int]): A tuple containing the height and width of the image (height, width).
+        shape (ShapeType): A tuple containing the height and width of the image (height, width).
 
     Returns:
         np.ndarray: A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels.
@@ -152,12 +152,12 @@
     if len(bboxes) == 0:
         return np.array([], dtype=np.float32)
 
-    height, width = image_shape
+    height, width = shape["height"], shape["width"]
     bboxes_denorm = bboxes.copy()
     bboxes_denorm[:, [0, 2]] *= width
     bboxes_denorm[:, [1, 3]] *= height
     return (bboxes_denorm[:, 2] - bboxes_denorm[:, 0]) * (bboxes_denorm[:, 3] - bboxes_denorm[:, 1])
-

def check_bboxes (bboxes) [view source on GitHub]¶

Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.

Parameters:

Name Type Description
bboxes np.ndarray

numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.

Exceptions:

Type Description
ValueError

If any bbox is invalid.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+

def check_bboxes (bboxes) [view source on GitHub]¶

Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.

Parameters:

Name Type Description
bboxes np.ndarray

numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.

Exceptions:

Type Description
ValueError

If any bbox is invalid.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
 def check_bboxes(bboxes: np.ndarray) -> None:
     """Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.
 
@@ -192,8 +192,8 @@
             raise ValueError(f"x_max is less than or equal to x_min for bbox {invalid_bbox}.")
 
         raise ValueError(f"y_max is less than or equal to y_min for bbox {invalid_bbox}.")
-

def clip_bboxes (bboxes, image_shape) [view source on GitHub]¶

Clips the bounding box coordinates to ensure they fit within the boundaries of an image.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (num_boxes, 4+) in normalized format. The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape Tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

The clipped bounding boxes, normalized to the image dimensions.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
-def clip_bboxes(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:
+

def clip_bboxes (bboxes, shape) [view source on GitHub]¶

Clips the bounding box coordinates to ensure they fit within the boundaries of an image.

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (num_boxes, 4+) in normalized format. The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape Tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

The clipped bounding boxes, normalized to the image dimensions.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+def clip_bboxes(bboxes: np.ndarray, shape: ShapeType) -> np.ndarray:
     """Clips the bounding box coordinates to ensure they fit within the boundaries of an image.
 
     Parameters:
@@ -205,10 +205,10 @@
         np.ndarray: The clipped bounding boxes, normalized to the image dimensions.
 
     """
-    height, width = image_shape[:2]
+    height, width = shape["height"], shape["width"]
 
     # Denormalize bboxes
-    denorm_bboxes = denormalize_bboxes(bboxes, image_shape)
+    denorm_bboxes = denormalize_bboxes(bboxes, shape)
 
     ## Note:
     # It could be tempting to use cols - 1 and rows - 1 as the upper bounds for the clipping
@@ -231,12 +231,12 @@
     denorm_bboxes[:, [1, 3]] = np.clip(denorm_bboxes[:, [1, 3]], 0, height, out=denorm_bboxes[:, [1, 3]])
 
     # Normalize clipped bboxes
-    return normalize_bboxes(denorm_bboxes, image_shape)
-

def convert_bboxes_from_albumentations (bboxes, target_format, image_shape, check_validity=False) [view source on GitHub]¶

Convert bounding boxes from the format used by albumentations to a specified format.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

target_format Literal['coco', 'pascal_voc', 'yolo']

Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.

image_shape tuple[int, int]

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description
np.ndarray

An array of bounding boxes in the target format with shape (num_bboxes, 4+).

Exceptions:

Type Description
ValueError

If target_format is not 'coco', 'pascal_voc' or 'yolo'.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+    return normalize_bboxes(denorm_bboxes, shape)
+

def convert_bboxes_from_albumentations (bboxes, target_format, shape, check_validity=False) [view source on GitHub]¶

Convert bounding boxes from the format used by albumentations to a specified format.

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

target_format Literal['coco', 'pascal_voc', 'yolo']

Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.

shape ShapeType

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description
np.ndarray

An array of bounding boxes in the target format with shape (num_bboxes, 4+).

Exceptions:

Type Description
ValueError

If target_format is not 'coco', 'pascal_voc' or 'yolo'.

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
 def convert_bboxes_from_albumentations(
     bboxes: np.ndarray,
     target_format: Literal["coco", "pascal_voc", "yolo"],
-    image_shape: tuple[int, int],
+    shape: ShapeType,
     check_validity: bool = False,
 ) -> np.ndarray:
     """Convert bounding boxes from the format used by albumentations to a specified format.
@@ -245,7 +245,7 @@
         bboxes: A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+).
                 The first 4 columns are [x_min, y_min, x_max, y_max].
         target_format: Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.
-        image_shape: Image shape (height, width).
+        shape: Image shape (height, width).
         check_validity: Check if all boxes are valid boxes.
 
     Returns:
@@ -265,7 +265,7 @@
     converted_bboxes = np.zeros_like(bboxes)
     converted_bboxes[:, 4:] = bboxes[:, 4:]  # Preserve additional columns
 
-    denormalized_bboxes = denormalize_bboxes(bboxes[:, :4], image_shape) if target_format != "yolo" else bboxes[:, :4]
+    denormalized_bboxes = denormalize_bboxes(bboxes[:, :4], shape) if target_format != "yolo" else bboxes[:, :4]
 
     if target_format == "coco":
         converted_bboxes[:, 0] = denormalized_bboxes[:, 0]  # x_min
@@ -281,11 +281,11 @@
         converted_bboxes[:, :4] = denormalized_bboxes
 
     return converted_bboxes
-

def convert_bboxes_to_albumentations (bboxes, source_format, image_shape, check_validity=False) [view source on GitHub]¶

Convert bounding boxes from a specified format to the format used by albumentations: normalized coordinates of top-left and bottom-right corners of the bounding box in the form of (x_min, y_min, x_max, y_max) e.g. (0.15, 0.27, 0.67, 0.5).

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+).

source_format Literal['coco', 'pascal_voc', 'yolo']

Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.

image_shape tuple[int, int]

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description
np.ndarray

An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).

Exceptions:

Type Description
ValueError

If source_format is not 'coco', 'pascal_voc', or 'yolo'.

ValueError

If in YOLO format, any coordinates are not in the range (0, 1].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+

def convert_bboxes_to_albumentations (bboxes, source_format, shape, check_validity=False) [view source on GitHub]¶

Convert bounding boxes from a specified format to the format used by albumentations: normalized coordinates of top-left and bottom-right corners of the bounding box in the form of (x_min, y_min, x_max, y_max) e.g. (0.15, 0.27, 0.67, 0.5).

Parameters:

Name Type Description
bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+).

source_format Literal['coco', 'pascal_voc', 'yolo']

Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.

shape ShapeType

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description
np.ndarray

An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).

Exceptions:

Type Description
ValueError

If source_format is not 'coco', 'pascal_voc', or 'yolo'.

ValueError

If in YOLO format, any coordinates are not in the range (0, 1].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
 def convert_bboxes_to_albumentations(
     bboxes: np.ndarray,
     source_format: Literal["coco", "pascal_voc", "yolo"],
-    image_shape: tuple[int, int],
+    shape: ShapeType,
     check_validity: bool = False,
 ) -> np.ndarray:
     """Convert bounding boxes from a specified format to the format used by albumentations:
@@ -295,7 +295,7 @@
     Args:
         bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).
         source_format: Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.
-        image_shape: Image shape (height, width).
+        shape: Image shape (height, width).
         check_validity: Check if all boxes are valid boxes.
 
     Returns:
@@ -332,36 +332,39 @@
         converted_bboxes[:, :4] = bboxes[:, :4]
 
     if source_format != "yolo":
-        converted_bboxes[:, :4] = normalize_bboxes(converted_bboxes[:, :4], image_shape)
+        converted_bboxes[:, :4] = normalize_bboxes(converted_bboxes[:, :4], shape)
 
     if check_validity:
         check_bboxes(converted_bboxes)
 
     return converted_bboxes
-

def denormalize_bboxes (bboxes, image_shape) [view source on GitHub]¶

Denormalize array of bounding boxes.

Parameters:

Name Type Description
bboxes np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

image_shape tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+

def denormalize_bboxes (bboxes, shape) [view source on GitHub]¶

Denormalize array of bounding boxes.

Parameters:

Name Type Description
bboxes np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

shape ShapeType | tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
 def denormalize_bboxes(
     bboxes: np.ndarray,
-    image_shape: tuple[int, int],
+    shape: ShapeType | tuple[int, int],
 ) -> np.ndarray:
     """Denormalize  array of bounding boxes.
 
     Args:
         bboxes: Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.
-        image_shape: Image shape `(height, width)`.
+        shape: Image shape `(height, width)`.
 
     Returns:
         Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.
 
     """
-    rows, cols = image_shape[:2]
-
-    denormalized = bboxes.copy().astype(float)
-    denormalized[:, [0, 2]] *= cols
-    denormalized[:, [1, 3]] *= rows
-    return denormalized
-

def filter_bboxes (bboxes, image_shape, min_area=0.0, min_visibility=0.0, min_width=1.0, min_height=1.0) [view source on GitHub]¶

Remove bounding boxes that either lie outside of the visible area by more than min_visibility or whose area in pixels is under the threshold set by min_area. Also crops boxes to final image size.

Parameters:

Name Type Description
bboxes np.ndarray

numpy array of bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Image shape (height, width).

min_area float

Minimum area of a bounding box in pixels. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels. Default: 0.0.

Returns:

Type Description
np.ndarray

numpy array of filtered bounding boxes.

Source code in albumentations/core/bbox_utils.py
Python
def filter_bboxes(
+    if isinstance(shape, tuple):
+        rows, cols = shape[:2]
+    else:
+        rows, cols = shape["height"], shape["width"]
+
+    denormalized = bboxes.copy().astype(float)
+    denormalized[:, [0, 2]] *= cols
+    denormalized[:, [1, 3]] *= rows
+    return denormalized
+

def filter_bboxes (bboxes, shape, min_area=0.0, min_visibility=0.0, min_width=1.0, min_height=1.0) [view source on GitHub]¶

Remove bounding boxes that either lie outside of the visible area by more than min_visibility or whose area in pixels is under the threshold set by min_area. Also crops boxes to final image size.

Parameters:

Name Type Description
bboxes np.ndarray

numpy array of bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

shape dict[str, int]

The shape of the image/volume: - For 2D: {'height': int, 'width': int} - For 3D: {'height': int, 'width': int, 'depth': int}

min_area float

Minimum area of a bounding box in pixels. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels. Default: 0.0.

Returns:

Type Description
np.ndarray

numpy array of filtered bounding boxes.

Source code in albumentations/core/bbox_utils.py
Python
def filter_bboxes(
     bboxes: np.ndarray,
-    image_shape: tuple[int, int],
+    shape: ShapeType,
     min_area: float = 0.0,
     min_visibility: float = 0.0,
     min_width: float = 1.0,
@@ -373,85 +376,96 @@
     Args:
         bboxes: numpy array of bounding boxes with shape (num_bboxes, 4+).
                 The first 4 columns are [x_min, y_min, x_max, y_max].
-        image_shape: Image shape (height, width).
-        min_area: Minimum area of a bounding box in pixels. Default: 0.0.
-        min_visibility: Minimum fraction of area for a bounding box to remain. Default: 0.0.
-        min_width: Minimum width of a bounding box in pixels. Default: 0.0.
-        min_height: Minimum height of a bounding box in pixels. Default: 0.0.
-
-    Returns:
-        numpy array of filtered bounding boxes.
-    """
-    epsilon = 1e-7
-
-    if len(bboxes) == 0:
-        return np.array([], dtype=np.float32).reshape(0, 4)
+        shape (dict[str, int]): The shape of the image/volume:
+                               - For 2D: {'height': int, 'width': int}
+                               - For 3D: {'height': int, 'width': int, 'depth': int}
+
+        min_area: Minimum area of a bounding box in pixels. Default: 0.0.
+        min_visibility: Minimum fraction of area for a bounding box to remain. Default: 0.0.
+        min_width: Minimum width of a bounding box in pixels. Default: 0.0.
+        min_height: Minimum height of a bounding box in pixels. Default: 0.0.
+
+    Returns:
+        numpy array of filtered bounding boxes.
+    """
+    epsilon = 1e-7
 
-    # Calculate areas of bounding boxes before clipping in pixels
-    denormalized_box_areas = calculate_bbox_areas_in_pixels(bboxes, image_shape)
+    if len(bboxes) == 0:
+        return np.array([], dtype=np.float32).reshape(0, 4)
 
-    # Clip bounding boxes in ratio
-    clipped_bboxes = clip_bboxes(bboxes, image_shape)
+    # Calculate areas of bounding boxes before clipping in pixels
+    denormalized_box_areas = calculate_bbox_areas_in_pixels(bboxes, shape)
 
-    # Calculate areas of clipped bounding boxes in pixels
-    clipped_box_areas = calculate_bbox_areas_in_pixels(clipped_bboxes, image_shape)
+    # Clip bounding boxes in ratio
+    clipped_bboxes = clip_bboxes(bboxes, shape)
 
-    # Calculate width and height of the clipped bounding boxes
-    denormalized_bboxes = denormalize_bboxes(clipped_bboxes[:, :4], image_shape)
+    # Calculate areas of clipped bounding boxes in pixels
+    clipped_box_areas = calculate_bbox_areas_in_pixels(clipped_bboxes, shape)
 
-    clipped_widths = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]
-    clipped_heights = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]
+    # Calculate width and height of the clipped bounding boxes
+    denormalized_bboxes = denormalize_bboxes(clipped_bboxes[:, :4], shape)
 
-    # Create a mask for bboxes that meet all criteria
-    mask = (
-        (denormalized_box_areas >= epsilon)
-        & (clipped_box_areas >= min_area - epsilon)
-        & (clipped_box_areas / denormalized_box_areas >= min_visibility - epsilon)
-        & (clipped_widths >= min_width - epsilon)
-        & (clipped_heights >= min_height - epsilon)
-    )
-
-    # Apply the mask to get the filtered bboxes
-    filtered_bboxes = clipped_bboxes[mask]
+    clipped_widths = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]
+    clipped_heights = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]
+
+    # Create a mask for bboxes that meet all criteria
+    mask = (
+        (denormalized_box_areas >= epsilon)
+        & (clipped_box_areas >= min_area - epsilon)
+        & (clipped_box_areas / denormalized_box_areas >= min_visibility - epsilon)
+        & (clipped_widths >= min_width - epsilon)
+        & (clipped_heights >= min_height - epsilon)
+    )
 
-    return np.array([], dtype=np.float32).reshape(0, 4) if len(filtered_bboxes) == 0 else filtered_bboxes
-

def masks_from_bboxes (bboxes, img_shape) [view source on GitHub]¶

Create binary masks from multiple bounding boxes

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (N, 4), where N is the number of boxes

img_shape tuple[int, int]

Image shape (height, width)

Returns:

Type Description
masks

Array of binary masks with shape (N, height, width)

Source code in albumentations/core/bbox_utils.py
Python
def masks_from_bboxes(bboxes: np.ndarray, img_shape: tuple[int, int]) -> np.ndarray:
+    # Apply the mask to get the filtered bboxes
+    filtered_bboxes = clipped_bboxes[mask]
+
+    return np.array([], dtype=np.float32).reshape(0, 4) if len(filtered_bboxes) == 0 else filtered_bboxes
+

def masks_from_bboxes (bboxes, shape) [view source on GitHub]¶

Create binary masks from multiple bounding boxes

Parameters:

Name Type Description
bboxes np.ndarray

Array of bounding boxes with shape (N, 4), where N is the number of boxes

shape ShapeType | tuple[int, int]

{"height": int, "width": int} or tuple[int, int]

Returns:

Type Description
masks

Array of binary masks with shape (N, height, width)

Source code in albumentations/core/bbox_utils.py
Python
def masks_from_bboxes(bboxes: np.ndarray, shape: ShapeType | tuple[int, int]) -> np.ndarray:
     """Create binary masks from multiple bounding boxes
 
     Args:
         bboxes: Array of bounding boxes with shape (N, 4), where N is the number of boxes
-        img_shape: Image shape (height, width)
+        shape: {"height": int, "width": int} or tuple[int, int]
 
     Returns:
         masks: Array of binary masks with shape (N, height, width)
 
     """
-    height, width = img_shape[:2]
-    masks = np.zeros((len(bboxes), height, width), dtype=np.uint8)
-    y, x = np.ogrid[:height, :width]
-
-    for i, (x_min, y_min, x_max, y_max) in enumerate(bboxes[:, :4].astype(int)):
-        masks[i] = (x_min <= x) & (x < x_max) & (y_min <= y) & (y < y_max)
-
-    return masks
-

def normalize_bboxes (bboxes, image_shape) [view source on GitHub]¶

Normalize array of bounding boxes.

Parameters:

Name Type Description
bboxes np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

image_shape tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
-def normalize_bboxes(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:
+    if isinstance(shape, dict):
+        height, width = shape["height"], shape["width"]
+    else:
+        height, width = shape[:2]
+
+    masks = np.zeros((len(bboxes), height, width), dtype=np.uint8)
+    y, x = np.ogrid[:height, :width]
+
+    for i, (x_min, y_min, x_max, y_max) in enumerate(bboxes[:, :4].astype(int)):
+        masks[i] = (x_min <= x) & (x < x_max) & (y_min <= y) & (y < y_max)
+
+    return masks
+

def normalize_bboxes (bboxes, shape) [view source on GitHub]¶

Normalize array of bounding boxes.

Parameters:

Name Type Description
bboxes np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

shape ShapeType | tuple[int, int]

Image shape (height, width).

Returns:

Type Description
np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py
Python
@handle_empty_array("bboxes")
+def normalize_bboxes(bboxes: np.ndarray, shape: ShapeType | tuple[int, int]) -> np.ndarray:
     """Normalize array of bounding boxes.
 
     Args:
         bboxes: Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.
-        image_shape: Image shape `(height, width)`.
+        shape: Image shape `(height, width)`.
 
     Returns:
         Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.
 
     """
-    rows, cols = image_shape[:2]
-    normalized = bboxes.copy().astype(float)
-    normalized[:, [0, 2]] /= cols
-    normalized[:, [1, 3]] /= rows
-    return normalized
-

def union_of_bboxes (bboxes, erosion_rate) [view source on GitHub]¶

Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.

Parameters:

Name Type Description
bboxes np.ndarray

List of bounding boxes

erosion_rate float

How much each bounding box can be shrunk, useful for erosive cropping. Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.

Returns:

Type Description
np.ndarray | None

A bounding box (x_min, y_min, x_max, y_max) or None if no bboxes are given or if the bounding boxes become invalid after erosion.

Source code in albumentations/core/bbox_utils.py
Python
def union_of_bboxes(bboxes: np.ndarray, erosion_rate: float) -> np.ndarray | None:
+    if isinstance(shape, tuple):
+        rows, cols = shape[:2]
+    else:
+        rows, cols = shape["height"], shape["width"]
+
+    normalized = bboxes.copy().astype(float)
+    normalized[:, [0, 2]] /= cols
+    normalized[:, [1, 3]] /= rows
+    return normalized
+

def union_of_bboxes (bboxes, erosion_rate) [view source on GitHub]¶

Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.

Parameters:

Name Type Description
bboxes np.ndarray

List of bounding boxes

erosion_rate float

How much each bounding box can be shrunk, useful for erosive cropping. Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.

Returns:

Type Description
np.ndarray | None

A bounding box (x_min, y_min, x_max, y_max) or None if no bboxes are given or if the bounding boxes become invalid after erosion.

Source code in albumentations/core/bbox_utils.py
Python
def union_of_bboxes(bboxes: np.ndarray, erosion_rate: float) -> np.ndarray | None:
     """Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.
 
     Args:
diff --git a/docs/api_reference/core/composition/index.html b/docs/api_reference/core/composition/index.html
index 429e67a4..201bfdbf 100644
--- a/docs/api_reference/core/composition/index.html
+++ b/docs/api_reference/core/composition/index.html
@@ -215,19 +215,27 @@
         for t in self.transforms:
             t.set_deterministic(flag, save_key)
 
-    def check_data_post_transform(self, data: Any) -> dict[str, Any]:
-        if self.check_each_transform:
-            image_shape = get_shape(data["image"])
-
-            for proc in self.check_each_transform:
-                for data_name in data:
-                    if data_name in proc.data_fields or (
-                        data_name in self._additional_targets
-                        and self._additional_targets[data_name] in proc.data_fields
-                    ):
-                        data[data_name] = proc.filter(data[data_name], image_shape)
-        return data
-

class Compose (transforms, bbox_params=None, keypoint_params=None, additional_targets=None, p=1.0, is_check_shapes=True, strict=True, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub] ¶

Compose multiple transforms together and apply them sequentially to input data.

This class allows you to chain multiple image augmentation transforms and apply them in a specified order. It also handles bounding box and keypoint transformations if the appropriate parameters are provided.

Parameters:

Name Type Description
transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to apply.

bbox_params Union[dict, BboxParams, None]

Parameters for bounding box transforms. Can be a dict of params or a BboxParams object. Default is None.

keypoint_params Union[dict, KeypointParams, None]

Parameters for keypoint transforms. Can be a dict of params or a KeypointParams object. Default is None.

additional_targets Dict[str, str]

A dictionary mapping additional target names to their types. For example, {'image2': 'image'}. Default is None.

p float

Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.

is_check_shapes bool

If True, checks consistency of shapes for image/mask/masks on each call. Disable only if you are sure about your data consistency. Default is True.

strict bool

If True, raises an error on unknown input keys. If False, ignores them. Default is True.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

seed int

Random seed. Default is None.

save_applied_params bool

If True, saves the applied parameters of each transform. Default is False.

Examples:

Python
>>> import albumentations as A
+    def check_data_post_transform(self, data: dict[str, Any]) -> dict[str, Any]:
+        """Check and filter data after transformation.
+
+        Args:
+            data: Dictionary containing transformed data
+
+        Returns:
+            Filtered data dictionary
+        """
+        if self.check_each_transform:
+            shape = get_shape(data)
+
+            for proc in self.check_each_transform:
+                for data_name, data_value in data.items():
+                    if data_name in proc.data_fields or (
+                        data_name in self._additional_targets
+                        and self._additional_targets[data_name] in proc.data_fields
+                    ):
+                        data[data_name] = proc.filter(data_value, shape)
+        return data
+

class Compose (transforms, bbox_params=None, keypoint_params=None, additional_targets=None, p=1.0, is_check_shapes=True, strict=True, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub] ¶

Compose multiple transforms together and apply them sequentially to input data.

This class allows you to chain multiple image augmentation transforms and apply them in a specified order. It also handles bounding box and keypoint transformations if the appropriate parameters are provided.

Parameters:

Name Type Description
transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to apply.

bbox_params Union[dict, BboxParams, None]

Parameters for bounding box transforms. Can be a dict of params or a BboxParams object. Default is None.

keypoint_params Union[dict, KeypointParams, None]

Parameters for keypoint transforms. Can be a dict of params or a KeypointParams object. Default is None.

additional_targets Dict[str, str]

A dictionary mapping additional target names to their types. For example, {'image2': 'image'}. Default is None.

p float

Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.

is_check_shapes bool

If True, checks consistency of shapes for image/mask/masks on each call. Disable only if you are sure about your data consistency. Default is True.

strict bool

If True, raises an error on unknown input keys. If False, ignores them. Default is True.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

seed int

Random seed. Default is None.

save_applied_params bool

If True, saves the applied parameters of each transform. Default is False.

Examples:

Python
>>> import albumentations as A
 >>> transform = A.Compose([
 ...     A.RandomCrop(width=256, height=256),
 ...     A.HorizontalFlip(p=0.5),
@@ -666,7 +674,7 @@
         if data.ndim not in [4, 5]:  # (N,D,H,W) or (N,D,H,W,C)
             raise TypeError(f"{data_name} must be 4D or 5D array")
         return data.shape[1:4]  # Return (D,H,W)
-

class OneOf (transforms, p=0.5) [view source on GitHub] ¶

Select one of transforms to apply. Selected transform will be called with force_apply=True. Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.

Parameters:

Name Type Description
transforms list

list of transformations to compose.

p float

probability of applying selected transform. Default: 0.5.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class OneOf(BaseCompose):
+

class OneOf (transforms, p=0.5) [view source on GitHub] ¶

Select one of transforms to apply. Selected transform will be called with force_apply=True. Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.

Parameters:

Name Type Description
transforms list

list of transformations to compose.

p float

probability of applying selected transform. Default: 0.5.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class OneOf(BaseCompose):
     """Select one of transforms to apply. Selected transform will be called with `force_apply=True`.
     Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.
 
@@ -694,7 +702,7 @@
             data = t(force_apply=True, **data)
             self._track_transform_params(t, data)
         return data
-

class OneOrOther (first=None, second=None, transforms=None, p=0.5) [view source on GitHub] ¶

Select one or another transform to apply. Selected transform will be called with force_apply=True.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class OneOrOther(BaseCompose):
+

class OneOrOther (first=None, second=None, transforms=None, p=0.5) [view source on GitHub] ¶

Select one or another transform to apply. Selected transform will be called with force_apply=True.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class OneOrOther(BaseCompose):
     """Select one or another transform to apply. Selected transform will be called with `force_apply=True`."""
 
     def __init__(
@@ -724,7 +732,7 @@
             return self.transforms[0](force_apply=True, **data)
 
         return self.transforms[-1](force_apply=True, **data)
-

class RandomOrder (transforms, n=1, replace=False, p=1) [view source on GitHub] ¶

Apply a random subset of transforms from the given list in a random order.

The RandomOrder class allows you to select a specified number of transforms from a list and apply them to the input data in a random order. This is useful for creating more diverse augmentation pipelines where the order of transformations can vary, potentially leading to different results.

Attributes:

Name Type Description
transforms TransformsSeqType

A list of transformations to choose from.

n int

The number of transforms to apply. If n is greater than the number of available transforms and replace is False, n will be set to the number of available transforms.

replace bool

Whether to sample transforms with replacement. If True, the same transform can be selected multiple times. Default is False.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

Examples:

Python
>>> import albumentations as A
+

class RandomOrder (transforms, n=1, replace=False, p=1) [view source on GitHub] ¶

Apply a random subset of transforms from the given list in a random order.

The RandomOrder class allows you to select a specified number of transforms from a list and apply them to the input data in a random order. This is useful for creating more diverse augmentation pipelines where the order of transformations can vary, potentially leading to different results.

Attributes:

Name Type Description
transforms TransformsSeqType

A list of transformations to choose from.

n int

The number of transforms to apply. If n is greater than the number of available transforms and replace is False, n will be set to the number of available transforms.

replace bool

Whether to sample transforms with replacement. If True, the same transform can be selected multiple times. Default is False.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

Examples:

Python
>>> import albumentations as A
 >>> transform = A.RandomOrder([
 ...     A.HorizontalFlip(p=1),
 ...     A.VerticalFlip(p=1),
@@ -771,7 +779,7 @@
             replace=self.replace,
             p=self.transforms_ps,
         )
-

class SelectiveChannelTransform (transforms, channels=(0, 1, 2), p=1.0) [view source on GitHub] ¶

A transformation class to apply specified transforms to selected channels of an image.

This class extends BaseCompose to allow selective application of transformations to specified image channels. It extracts the selected channels, applies the transformations, and then reinserts the transformed channels back into their original positions in the image.

Parameters:

Name Type Description
transforms TransformsSeqType

A sequence of transformations (from Albumentations) to be applied to the specified channels.

channels Sequence[int]

A sequence of integers specifying the indices of the channels to which the transforms should be applied.

p float

Probability that the transform will be applied; the default is 1.0 (always apply).

Methods

call(args, *kwargs): Applies the transforms to the image according to the specified channels. The input data should include 'image' key with the image array.

Returns:

Type Description
dict[str, Any]

The transformed data dictionary, which includes the transformed 'image' key.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class SelectiveChannelTransform(BaseCompose):
+

class SelectiveChannelTransform (transforms, channels=(0, 1, 2), p=1.0) [view source on GitHub] ¶

A transformation class to apply specified transforms to selected channels of an image.

This class extends BaseCompose to allow selective application of transformations to specified image channels. It extracts the selected channels, applies the transformations, and then reinserts the transformed channels back into their original positions in the image.

Parameters:

Name Type Description
transforms TransformsSeqType

A sequence of transformations (from Albumentations) to be applied to the specified channels.

channels Sequence[int]

A sequence of integers specifying the indices of the channels to which the transforms should be applied.

p float

Probability that the transform will be applied; the default is 1.0 (always apply).

Methods

call(args, *kwargs): Applies the transforms to the image according to the specified channels. The input data should include 'image' key with the image array.

Returns:

Type Description
dict[str, Any]

The transformed data dictionary, which includes the transformed 'image' key.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py
Python
class SelectiveChannelTransform(BaseCompose):
     """A transformation class to apply specified transforms to selected channels of an image.
 
     This class extends BaseCompose to allow selective application of transformations to
@@ -824,7 +832,7 @@
             data["image"] = np.ascontiguousarray(output_img)
 
         return data
-

class Sequential (transforms, p=0.5) [view source on GitHub] ¶

Sequentially applies all transforms to targets.

Note

This transform is not intended to be a replacement for Compose. Instead, it should be used inside Compose the same way OneOf or OneOrOther are used. For instance, you can combine OneOf with Sequential to create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly chose sequence to input data (see the Example section for an example definition of such pipeline).

Examples:

Python
>>> import albumentations as A
+

class Sequential (transforms, p=0.5) [view source on GitHub] ¶

Sequentially applies all transforms to targets.

Note

This transform is not intended to be a replacement for Compose. Instead, it should be used inside Compose the same way OneOf or OneOrOther are used. For instance, you can combine OneOf with Sequential to create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly chose sequence to input data (see the Example section for an example definition of such pipeline).

Examples:

Python
>>> import albumentations as A
 >>> transform = A.Compose([
 >>>    A.OneOf([
 >>>        A.Sequential([
@@ -873,7 +881,7 @@
                 self._track_transform_params(t, data)
                 data = self.check_data_post_transform(data)
         return data
-

class SomeOf (transforms, n=1, replace=False, p=1) [view source on GitHub] ¶

Apply a random subset of transforms from the given list.

This class selects a specified number of transforms from the provided list and applies them to the input data. The selection can be done with or without replacement, allowing for the same transform to be potentially applied multiple times.

Parameters:

Name Type Description
transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to choose from.

n int

The number of transforms to apply. If greater than the number of transforms and replace=False, it will be set to the number of transforms.

replace bool

Whether to sample transforms with replacement. Default is True.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

Note

  • If n is greater than the number of transforms and replace is False, n will be set to the number of transforms with a warning.
  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.

Examples:

Python
>>> import albumentations as A
+

class SomeOf (transforms, n=1, replace=False, p=1) [view source on GitHub] ¶

Apply a random subset of transforms from the given list.

This class selects a specified number of transforms from the provided list and applies them to the input data. The selection can be done with or without replacement, allowing for the same transform to be potentially applied multiple times.

Parameters:

Name Type Description
transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to choose from.

n int

The number of transforms to apply. If greater than the number of transforms and replace=False, it will be set to the number of transforms.

replace bool

Whether to sample transforms with replacement. Default is True.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

Note

  • If n is greater than the number of transforms and replace is False, n will be set to the number of transforms with a warning.
  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.

Examples:

Python
>>> import albumentations as A
 >>> transform = A.SomeOf([
 ...     A.HorizontalFlip(p=1),
 ...     A.VerticalFlip(p=1),
diff --git a/docs/api_reference/core/keypoints_utils/index.html b/docs/api_reference/core/keypoints_utils/index.html
index b2e3107d..6dcf281c 100644
--- a/docs/api_reference/core/keypoints_utils/index.html
+++ b/docs/api_reference/core/keypoints_utils/index.html
@@ -6,183 +6,270 @@
   .jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon {
     font-size: 0.8rem;
   }
-    

Helper functions for working with keypoints (augmentations.core.keypoints_utils)

class KeypointParams (format, label_fields=None, remove_invisible=True, angle_in_degrees=True, check_each_transform=True) [view source on GitHub] ¶

Parameters of keypoints

Parameters:

Name Type Description
format str

format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa'.

x - X coordinate,

y - Y coordinate

s - Keypoint scale

a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)

label_fields list

list of fields that are joined with keypoints, e.g labels. Should be same type as keypoints.

remove_invisible bool

to remove invisible points after transform or not

angle_in_degrees bool

angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints

check_each_transform bool

if True, then keypoints will be checked after each dual transform. Default: True

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py
Python
class KeypointParams(Params):
+    

Helper functions for working with keypoints (augmentations.core.keypoints_utils)

class KeypointParams (format, label_fields=None, remove_invisible=True, angle_in_degrees=True, check_each_transform=True) [view source on GitHub] ¶

Parameters of keypoints

Parameters:

Name Type Description
format str

format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa', 'xyz'.

x - X coordinate,

y - Y coordinate

z - Z coordinate (for 3D keypoints)

s - Keypoint scale

a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)

label_fields list

list of fields that are joined with keypoints, e.g labels. Should be same type as keypoints.

remove_invisible bool

to remove invisible points after transform or not

angle_in_degrees bool

angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints

check_each_transform bool

if True, then keypoints will be checked after each dual transform. Default: True

Note

The internal Albumentations format is [x, y, z, angle, scale]. For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0. For formats without angle or scale, these values are set to 0.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py
Python
class KeypointParams(Params):
     """Parameters of keypoints
 
     Args:
-        format (str): format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa'.
+        format (str): format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa', 'xyz'.
 
             x - X coordinate,
 
             y - Y coordinate
 
-            s - Keypoint scale
+            z - Z coordinate (for 3D keypoints)
 
-            a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)
-        label_fields (list): list of fields that are joined with keypoints, e.g labels.
-            Should be same type as keypoints.
-        remove_invisible (bool): to remove invisible points after transform or not
-        angle_in_degrees (bool): angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints
-        check_each_transform (bool): if `True`, then keypoints will be checked after each dual transform.
-            Default: `True`
+            s - Keypoint scale
+
+            a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)
+
+        label_fields (list): list of fields that are joined with keypoints, e.g labels.
+            Should be same type as keypoints.
+        remove_invisible (bool): to remove invisible points after transform or not
+        angle_in_degrees (bool): angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints
+        check_each_transform (bool): if `True`, then keypoints will be checked after each dual transform.
+            Default: `True`
+
+    Note:
+        The internal Albumentations format is [x, y, z, angle, scale]. For 2D formats (xy, yx, xya, xys, xyas, xysa),
+        z coordinate is set to 0. For formats without angle or scale, these values are set to 0.
+    """
+
+    def __init__(
+        self,
+        format: str,  # noqa: A002
+        label_fields: Sequence[str] | None = None,
+        remove_invisible: bool = True,
+        angle_in_degrees: bool = True,
+        check_each_transform: bool = True,
+    ):
+        super().__init__(format, label_fields)
+        self.remove_invisible = remove_invisible
+        self.angle_in_degrees = angle_in_degrees
+        self.check_each_transform = check_each_transform
+
+    def to_dict_private(self) -> dict[str, Any]:
+        data = super().to_dict_private()
+        data.update(
+            {
+                "remove_invisible": self.remove_invisible,
+                "angle_in_degrees": self.angle_in_degrees,
+                "check_each_transform": self.check_each_transform,
+            },
+        )
+        return data
+
+    @classmethod
+    def is_serializable(cls) -> bool:
+        return True
+
+    @classmethod
+    def get_class_fullname(cls) -> str:
+        return "KeypointParams"
+
+    def __repr__(self) -> str:
+        return (
+            f"KeypointParams(format={self.format}, label_fields={self.label_fields},"
+            f" remove_invisible={self.remove_invisible}, angle_in_degrees={self.angle_in_degrees},"
+            f" check_each_transform={self.check_each_transform})"
+        )
+

class KeypointsProcessor (params, additional_targets=None) [view source on GitHub] ¶

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py
Python
class KeypointsProcessor(DataProcessor):
+    def __init__(self, params: KeypointParams, additional_targets: dict[str, str] | None = None):
+        super().__init__(params, additional_targets)
+
+    @property
+    def default_data_name(self) -> str:
+        return "keypoints"
+
+    def ensure_data_valid(self, data: dict[str, Any]) -> None:
+        if self.params.label_fields and not all(i in data for i in self.params.label_fields):
+            msg = "Your 'label_fields' are not valid - them must have same names as params in 'keypoint_params' dict"
+            raise ValueError(msg)
+
+    def filter(
+        self,
+        data: np.ndarray,
+        shape: ShapeType,
+    ) -> np.ndarray:
+        """Filter keypoints based on visibility within given shape.
 
-    """
-
-    def __init__(
-        self,
-        format: str,  # noqa: A002
-        label_fields: Sequence[str] | None = None,
-        remove_invisible: bool = True,
-        angle_in_degrees: bool = True,
-        check_each_transform: bool = True,
-    ):
-        super().__init__(format, label_fields)
-        self.remove_invisible = remove_invisible
-        self.angle_in_degrees = angle_in_degrees
-        self.check_each_transform = check_each_transform
-
-    def to_dict_private(self) -> dict[str, Any]:
-        data = super().to_dict_private()
-        data.update(
-            {
-                "remove_invisible": self.remove_invisible,
-                "angle_in_degrees": self.angle_in_degrees,
-                "check_each_transform": self.check_each_transform,
-            },
-        )
-        return data
-
-    @classmethod
-    def is_serializable(cls) -> bool:
-        return True
+        Args:
+            data: Keypoints in [x, y, z, angle, scale] format
+            shape: Shape to check against as {'height': height, 'width': width, 'depth': depth}
+
+        Returns:
+            Filtered keypoints
+        """
+        self.params: KeypointParams
+        return filter_keypoints(data, shape, remove_invisible=self.params.remove_invisible)
+
+    def check(self, data: np.ndarray, shape: ShapeType) -> None:
+        check_keypoints(data, shape)
+
+    def convert_from_albumentations(
+        self,
+        data: np.ndarray,
+        shape: ShapeType,
+    ) -> np.ndarray:
+        if not data.size:
+            return data
+
+        params = self.params
+        return convert_keypoints_from_albumentations(
+            data,
+            params.format,
+            shape,
+            check_validity=params.remove_invisible,
+            angle_in_degrees=params.angle_in_degrees,
+        )
 
-    @classmethod
-    def get_class_fullname(cls) -> str:
-        return "KeypointParams"
-
-    def __repr__(self) -> str:
-        return (
-            f"KeypointParams(format={self.format}, label_fields={self.label_fields},"
-            f" remove_invisible={self.remove_invisible}, angle_in_degrees={self.angle_in_degrees},"
-            f" check_each_transform={self.check_each_transform})"
-        )
-

def check_keypoints (keypoints, image_shape) [view source on GitHub]¶

Check if keypoint coordinates are within valid ranges for the given image shape.

This function validates that: 1. All x-coordinates are within [0, width) 2. All y-coordinates are within [0, height) 3. If angles are present (i.e., keypoints have more than 2 columns), they are within the range [0, 2Ï€)

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. Each row represents a keypoint with at least (x, y) coordinates. If present, the third column is assumed to be the angle.

image_shape Tuple[int, int]

The shape of the image (height, width).

Exceptions:

Type Description
ValueError

If any keypoint coordinate is outside the valid range, or if any angle is invalid. The error message will detail which keypoints are invalid and why.

Note

  • The function assumes that keypoint coordinates are in absolute pixel values, not normalized.
  • Angles, if present, are assumed to be in radians.
  • The constant PAIR should be defined elsewhere in the module, typically as 2.
Source code in albumentations/core/keypoints_utils.py
Python
def check_keypoints(keypoints: np.ndarray, image_shape: tuple[int, int]) -> None:
-    """Check if keypoint coordinates are within valid ranges for the given image shape.
+    def convert_to_albumentations(
+        self,
+        data: np.ndarray,
+        shape: ShapeType,
+    ) -> np.ndarray:
+        if not data.size:
+            return data
+        params = self.params
+        return convert_keypoints_to_albumentations(
+            data,
+            params.format,
+            shape,
+            check_validity=params.remove_invisible,
+            angle_in_degrees=params.angle_in_degrees,
+        )
+

def check_keypoints (keypoints, shape) [view source on GitHub]¶

Check if keypoint coordinates are within valid ranges for the given shape.

This function validates that: 1. All x-coordinates are within [0, width) 2. All y-coordinates are within [0, height) 3. If depth is provided in shape, z-coordinates are within [0, depth) 4. Angles are within the range [0, 2Ï€)

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 5+), where N is the number of keypoints. - First 2 columns are always x, y - Column 3 (if present) is z - Column 4 (if present) is angle - Column 5+ (if present) are additional attributes

shape ShapeType

The shape of the image/volume: - For 2D: {'height': int, 'width': int} - For 3D: {'height': int, 'width': int, 'depth': int}

Exceptions:

Type Description
ValueError

If any keypoint coordinate is outside the valid range, or if angles are invalid. The error message will detail which keypoints are invalid and why.

Note

  • The function assumes that keypoint coordinates are in absolute pixel values, not normalized
  • Angles are in radians
  • Z-coordinates are only checked if 'depth' is present in shape
Source code in albumentations/core/keypoints_utils.py
Python
def check_keypoints(keypoints: np.ndarray, shape: ShapeType) -> None:
+    """Check if keypoint coordinates are within valid ranges for the given shape.
 
     This function validates that:
     1. All x-coordinates are within [0, width)
     2. All y-coordinates are within [0, height)
-    3. If angles are present (i.e., keypoints have more than 2 columns),
-       they are within the range [0, 2Ï€)
+    3. If depth is provided in shape, z-coordinates are within [0, depth)
+    4. Angles are within the range [0, 2Ï€)
 
     Args:
-        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.
-                                Each row represents a keypoint with at least (x, y) coordinates.
-                                If present, the third column is assumed to be the angle.
-        image_shape (Tuple[int, int]): The shape of the image (height, width).
-
-    Raises:
-        ValueError: If any keypoint coordinate is outside the valid range, or if any angle is invalid.
-                    The error message will detail which keypoints are invalid and why.
+        keypoints (np.ndarray): Array of keypoints with shape (N, 5+), where N is the number of keypoints.
+            - First 2 columns are always x, y
+            - Column 3 (if present) is z
+            - Column 4 (if present) is angle
+            - Column 5+ (if present) are additional attributes
+        shape (ShapeType): The shape of the image/volume:
+                           - For 2D: {'height': int, 'width': int}
+                           - For 3D: {'height': int, 'width': int, 'depth': int}
 
-    Note:
-        - The function assumes that keypoint coordinates are in absolute pixel values, not normalized.
-        - Angles, if present, are assumed to be in radians.
-        - The constant PAIR should be defined elsewhere in the module, typically as 2.
-    """
-    height, width = image_shape[:2]
-
-    # Check x and y coordinates
-    x, y = keypoints[:, 0], keypoints[:, 1]
-    if np.any((x < 0) | (x >= width)) or np.any((y < 0) | (y >= height)):
-        invalid_x = np.where((x < 0) | (x >= width))[0]
-        invalid_y = np.where((y < 0) | (y >= height))[0]
-
-        error_messages = []
-
-        error_messages = [
-            f"Expected {'x' if idx in invalid_x else 'y'} for keypoint {keypoints[idx]} to be "
-            f"in the range [0.0, {width if idx in invalid_x else height}], "
-            f"got {x[idx] if idx in invalid_x else y[idx]}."
-            for idx in sorted(set(invalid_x) | set(invalid_y))
-        ]
-
-        raise ValueError("\n".join(error_messages))
-
-    # Check angles
-    if keypoints.shape[1] > PAIR:
-        angles = keypoints[:, 2]
-        invalid_angles = np.where((angles < 0) | (angles >= 2 * math.pi))[0]
-        if len(invalid_angles) > 0:
-            error_messages = [
-                f"Keypoint angle must be in range [0, 2 * PI). Got: {angles[idx]} for keypoint {keypoints[idx]}"
-                for idx in invalid_angles
-            ]
-            raise ValueError("\n".join(error_messages))
-

def convert_keypoints_from_albumentations (keypoints, target_format, image_shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]¶

Convert keypoints from Albumentations format to various other formats.

This function takes keypoints in the standard Albumentations format [x, y, angle, scale] and converts them to the specified target format.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints in Albumentations format with shape (N, 4+), where N is the number of keypoints. Each row represents a keypoint [x, y, angle, scale, ...].

target_format Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]

The desired output format. - "xy": [x, y] - "yx": [y, x] - "xya": [x, y, angle] - "xys": [x, y, scale] - "xyas": [x, y, angle, scale] - "xysa": [x, y, scale, angle]

image_shape tuple[int, int]

The shape of the image (height, width).

check_validity bool

If True, check if the keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert output angles to degrees. If False, angles remain in radians. Defaults to True.

Returns:

Type Description
np.ndarray

Array of keypoints in the specified target format with shape (N, 2+). Any additional columns from the input keypoints beyond the first 4 are preserved and appended after the converted columns.

Exceptions:

Type Description
ValueError

If the target_format is not one of the supported formats.

Note

  • Input angles are assumed to be in the range [0, 2Ï€) radians.
  • If the input keypoints have additional columns beyond the first 4, these columns are preserved in the output.
  • The constant NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS should be defined elsewhere in the module, typically as 4.
Source code in albumentations/core/keypoints_utils.py
Python
def convert_keypoints_from_albumentations(
+    Raises:
+        ValueError: If any keypoint coordinate is outside the valid range, or if angles are invalid.
+                   The error message will detail which keypoints are invalid and why.
+
+    Note:
+        - The function assumes that keypoint coordinates are in absolute pixel values, not normalized
+        - Angles are in radians
+        - Z-coordinates are only checked if 'depth' is present in shape
+    """
+    height, width = shape["height"], shape["width"]
+    has_depth = "depth" in shape
+
+    # Check x and y coordinates (always present)
+    x, y = keypoints[:, 0], keypoints[:, 1]
+    invalid_x = np.where((x < 0) | (x >= width))[0]
+    invalid_y = np.where((y < 0) | (y >= height))[0]
+
+    error_messages = []
+
+    # Handle x, y errors
+    for idx in sorted(set(invalid_x) | set(invalid_y)):
+        if idx in invalid_x:
+            error_messages.append(
+                f"Expected x for keypoint {keypoints[idx]} to be in range [0, {width}), got {x[idx]}",
+            )
+        if idx in invalid_y:
+            error_messages.append(
+                f"Expected y for keypoint {keypoints[idx]} to be in range [0, {height}), got {y[idx]}",
+            )
+
+    # Check z coordinates if depth is provided and keypoints have z
+    if has_depth and keypoints.shape[1] > 2:
+        z = keypoints[:, 2]
+        depth = shape["depth"]
+        invalid_z = np.where((z < 0) | (z >= depth))[0]
+        error_messages.extend(
+            f"Expected z for keypoint {keypoints[idx]} to be in range [0, {depth}), got {z[idx]}" for idx in invalid_z
+        )
+
+    # Check angles only if keypoints have angle column
+    if keypoints.shape[1] > 3:
+        angles = keypoints[:, 3]
+        invalid_angles = np.where((angles < 0) | (angles >= 2 * math.pi))[0]
+        error_messages.extend(
+            f"Expected angle for keypoint {keypoints[idx]} to be in range [0, 2Ï€), got {angles[idx]}"
+            for idx in invalid_angles
+        )
+
+    if error_messages:
+        raise ValueError("\n".join(error_messages))
+

def convert_keypoints_from_albumentations (keypoints, target_format, shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]¶

Convert keypoints from Albumentations format to various other formats.

This function takes keypoints in the standard Albumentations format [x, y, z, angle, scale] and converts them to the specified target format.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints in Albumentations format with shape (N, 5+), where N is the number of keypoints. Each row represents a keypoint [x, y, z, angle, scale, ...].

target_format Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"]

The desired output format. - "xy": [x, y] - "yx": [y, x] - "xya": [x, y, angle] - "xys": [x, y, scale] - "xyas": [x, y, angle, scale] - "xysa": [x, y, scale, angle] - "xyz": [x, y, z]

shape ShapeType

The shape of the image {'height': height, 'width': width, 'depth': depth}.

check_validity bool

If True, check if the keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert output angles to degrees. If False, angles remain in radians. Defaults to True.

Returns:

Type Description
np.ndarray

Array of keypoints in the specified target format with shape (N, 2+). Any additional columns from the input keypoints beyond the first 5 are preserved and appended after the converted columns.

Exceptions:

Type Description
ValueError

If the target_format is not one of the supported formats.

Note

  • Input angles are assumed to be in the range [0, 2Ï€) radians
  • If the input keypoints have additional columns beyond the first 5, these columns are preserved in the output
Source code in albumentations/core/keypoints_utils.py
Python
def convert_keypoints_from_albumentations(
     keypoints: np.ndarray,
-    target_format: Literal["xy", "yx", "xya", "xys", "xyas", "xysa"],
-    image_shape: tuple[int, int],
+    target_format: Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"],
+    shape: ShapeType,
     check_validity: bool = False,
     angle_in_degrees: bool = True,
 ) -> np.ndarray:
     """Convert keypoints from Albumentations format to various other formats.
 
-    This function takes keypoints in the standard Albumentations format [x, y, angle, scale]
+    This function takes keypoints in the standard Albumentations format [x, y, z, angle, scale]
     and converts them to the specified target format.
 
     Args:
-        keypoints (np.ndarray): Array of keypoints in Albumentations format with shape (N, 4+),
+        keypoints (np.ndarray): Array of keypoints in Albumentations format with shape (N, 5+),
                                 where N is the number of keypoints. Each row represents a keypoint
-                                [x, y, angle, scale, ...].
-        target_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]): The desired output format.
+                                [x, y, z, angle, scale, ...].
+        target_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"]): The desired output format.
             - "xy": [x, y]
             - "yx": [y, x]
             - "xya": [x, y, angle]
             - "xys": [x, y, scale]
             - "xyas": [x, y, angle, scale]
             - "xysa": [x, y, scale, angle]
-        image_shape (tuple[int, int]): The shape of the image (height, width).
-        check_validity (bool, optional): If True, check if the keypoints are within the image boundaries.
-                                         Defaults to False.
-        angle_in_degrees (bool, optional): If True, convert output angles to degrees.
-                                           If False, angles remain in radians.
-                                           Defaults to True.
-
-    Returns:
-        np.ndarray: Array of keypoints in the specified target format with shape (N, 2+).
-                    Any additional columns from the input keypoints beyond the first 4
-                    are preserved and appended after the converted columns.
-
-    Raises:
-        ValueError: If the target_format is not one of the supported formats.
-
-    Note:
-        - Input angles are assumed to be in the range [0, 2Ï€) radians.
-        - If the input keypoints have additional columns beyond the first 4,
-          these columns are preserved in the output.
-        - The constant NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS should be defined
-          elsewhere in the module, typically as 4.
-    """
-    if target_format not in keypoint_formats:
-        raise ValueError(f"Unknown target_format {target_format}. Supported formats are: {keypoint_formats}")
-
-    x, y, angle, scale = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2], keypoints[:, 3]
-    angle = angle_to_2pi_range(angle)
-
-    if check_validity:
-        check_keypoints(np.column_stack((x, y, angle, scale)), image_shape)
-
-    if angle_in_degrees:
-        angle = np.degrees(angle)
-
-    format_to_columns = {
-        "xy": [x, y],
-        "yx": [y, x],
-        "xya": [x, y, angle],
-        "xys": [x, y, scale],
-        "xyas": [x, y, angle, scale],
-        "xysa": [x, y, scale, angle],
+            - "xyz": [x, y, z]
+        shape (ShapeType): The shape of the image {'height': height, 'width': width, 'depth': depth}.
+        check_validity (bool, optional): If True, check if the keypoints are within the image boundaries.
+                                         Defaults to False.
+        angle_in_degrees (bool, optional): If True, convert output angles to degrees.
+                                           If False, angles remain in radians.
+                                           Defaults to True.
+
+    Returns:
+        np.ndarray: Array of keypoints in the specified target format with shape (N, 2+).
+                    Any additional columns from the input keypoints beyond the first 5
+                    are preserved and appended after the converted columns.
+
+    Raises:
+        ValueError: If the target_format is not one of the supported formats.
+
+    Note:
+        - Input angles are assumed to be in the range [0, 2Ï€) radians
+        - If the input keypoints have additional columns beyond the first 5,
+          these columns are preserved in the output
+    """
+    if target_format not in keypoint_formats:
+        raise ValueError(f"Unknown target_format {target_format}. Supported formats are: {keypoint_formats}")
+
+    x, y, z, angle, scale = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2], keypoints[:, 3], keypoints[:, 4]
+    angle = angle_to_2pi_range(angle)
+
+    if check_validity:
+        check_keypoints(np.column_stack((x, y, z, angle, scale)), shape)
+
+    if angle_in_degrees:
+        angle = np.degrees(angle)
+
+    format_to_columns = {
+        "xy": [x, y],
+        "yx": [y, x],
+        "xya": [x, y, angle],
+        "xys": [x, y, scale],
+        "xyas": [x, y, angle, scale],
+        "xysa": [x, y, scale, angle],
+        "xyz": [x, y, z],
     }
 
     result = np.column_stack(format_to_columns[target_format])
@@ -192,92 +279,95 @@
         return np.column_stack((result, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]))
 
     return result
-

def convert_keypoints_to_albumentations (keypoints, source_format, image_shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]¶

Convert keypoints from various formats to the Albumentations format.

This function takes keypoints in different formats and converts them to the standard Albumentations format: [x, y, angle, scale]. If the input format doesn't include angle or scale, these values are set to 0.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. The number of columns depends on the source_format.

source_format Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]

The format of the input keypoints. - "xy": [x, y] - "yx": [y, x] - "xya": [x, y, angle] - "xys": [x, y, scale] - "xyas": [x, y, angle, scale] - "xysa": [x, y, scale, angle]

image_shape tuple[int, int]

The shape of the image (height, width).

check_validity bool

If True, check if the converted keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert input angles from degrees to radians. Defaults to True.

Returns:

Type Description
np.ndarray

Array of keypoints in Albumentations format [x, y, angle, scale] with shape (N, 4+). Any additional columns from the input keypoints are preserved and appended after the first 4 columns.

Exceptions:

Type Description
ValueError

If the source_format is not one of the supported formats.

Note

  • Angles are converted to the range [0, 2Ï€) radians.
  • If the input keypoints have additional columns beyond what's specified in the source_format, these columns are preserved in the output.
Source code in albumentations/core/keypoints_utils.py
Python
def convert_keypoints_to_albumentations(
+

def convert_keypoints_to_albumentations (keypoints, source_format, shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]¶

Convert keypoints from various formats to the Albumentations format.

This function takes keypoints in different formats and converts them to the standard Albumentations format: [x, y, z, angle, scale]. For 2D formats, z is set to 0. For formats without angle or scale, these values are set to 0.

Parameters:

Name Type Description
keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. The number of columns depends on the source_format.

source_format Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"]

The format of the input keypoints. - "xy": [x, y] - "yx": [y, x] - "xya": [x, y, angle] - "xys": [x, y, scale] - "xyas": [x, y, angle, scale] - "xysa": [x, y, scale, angle] - "xyz": [x, y, z]

shape ShapeType

The shape of the image {'height': height, 'width': width, 'depth': depth}.

check_validity bool

If True, check if the converted keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert input angles from degrees to radians. Defaults to True.

Returns:

Type Description
np.ndarray

Array of keypoints in Albumentations format [x, y, z, angle, scale] with shape (N, 5+). Any additional columns from the input keypoints are preserved and appended after the first 5 columns.

Exceptions:

Type Description
ValueError

If the source_format is not one of the supported formats.

Note

  • For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0
  • Angles are converted to the range [0, 2Ï€) radians
  • If the input keypoints have additional columns beyond what's specified in the source_format, these columns are preserved in the output
Source code in albumentations/core/keypoints_utils.py
Python
def convert_keypoints_to_albumentations(
     keypoints: np.ndarray,
-    source_format: Literal["xy", "yx", "xya", "xys", "xyas", "xysa"],
-    image_shape: tuple[int, int],
+    source_format: Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"],
+    shape: ShapeType,
     check_validity: bool = False,
     angle_in_degrees: bool = True,
 ) -> np.ndarray:
     """Convert keypoints from various formats to the Albumentations format.
 
     This function takes keypoints in different formats and converts them to the standard
-    Albumentations format: [x, y, angle, scale]. If the input format doesn't include
-    angle or scale, these values are set to 0.
+    Albumentations format: [x, y, z, angle, scale]. For 2D formats, z is set to 0.
+    For formats without angle or scale, these values are set to 0.
 
     Args:
         keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.
                                 The number of columns depends on the source_format.
-        source_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]): The format of the input keypoints.
+        source_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa", "xyz"]): The format of the input keypoints.
             - "xy": [x, y]
             - "yx": [y, x]
             - "xya": [x, y, angle]
             - "xys": [x, y, scale]
             - "xyas": [x, y, angle, scale]
             - "xysa": [x, y, scale, angle]
-        image_shape (tuple[int, int]): The shape of the image (height, width).
-        check_validity (bool, optional): If True, check if the converted keypoints are within the image boundaries.
-                                         Defaults to False.
-        angle_in_degrees (bool, optional): If True, convert input angles from degrees to radians.
-                                           Defaults to True.
-
-    Returns:
-        np.ndarray: Array of keypoints in Albumentations format [x, y, angle, scale] with shape (N, 4+).
-                    Any additional columns from the input keypoints are preserved and appended after the
-                    first 4 columns.
-
-    Raises:
-        ValueError: If the source_format is not one of the supported formats.
-
-    Note:
-        - Angles are converted to the range [0, 2Ï€) radians.
-        - If the input keypoints have additional columns beyond what's specified in the source_format,
-          these columns are preserved in the output.
-    """
-    if source_format not in keypoint_formats:
-        raise ValueError(f"Unknown source_format {source_format}. Supported formats are: {keypoint_formats}")
-
-    format_to_indices: dict[str, list[int | None]] = {
-        "xy": [0, 1, None, None],
-        "yx": [1, 0, None, None],
-        "xya": [0, 1, 2, None],
-        "xys": [0, 1, None, 2],
-        "xyas": [0, 1, 2, 3],
-        "xysa": [0, 1, 3, 2],
-    }
-
-    indices: list[int | None] = format_to_indices[source_format]
-
-    processed_keypoints = np.zeros((keypoints.shape[0], NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS), dtype=np.float32)
-
-    for i, idx in enumerate(indices):
-        if idx is not None:
-            processed_keypoints[:, i] = keypoints[:, idx]
-
-    if angle_in_degrees and indices[2] is not None:
-        processed_keypoints[:, 2] = np.radians(processed_keypoints[:, 2])
+            - "xyz": [x, y, z]
+        shape (ShapeType): The shape of the image {'height': height, 'width': width, 'depth': depth}.
+        check_validity (bool, optional): If True, check if the converted keypoints are within the image boundaries.
+                                         Defaults to False.
+        angle_in_degrees (bool, optional): If True, convert input angles from degrees to radians.
+                                           Defaults to True.
+
+    Returns:
+        np.ndarray: Array of keypoints in Albumentations format [x, y, z, angle, scale] with shape (N, 5+).
+                    Any additional columns from the input keypoints are preserved and appended after the
+                    first 5 columns.
+
+    Raises:
+        ValueError: If the source_format is not one of the supported formats.
+
+    Note:
+        - For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0
+        - Angles are converted to the range [0, 2Ï€) radians
+        - If the input keypoints have additional columns beyond what's specified in the source_format,
+          these columns are preserved in the output
+    """
+    if source_format not in keypoint_formats:
+        raise ValueError(f"Unknown source_format {source_format}. Supported formats are: {keypoint_formats}")
+
+    format_to_indices: dict[str, list[int | None]] = {
+        "xy": [0, 1, None, None, None],
+        "yx": [1, 0, None, None, None],
+        "xya": [0, 1, None, 2, None],
+        "xys": [0, 1, None, None, 2],
+        "xyas": [0, 1, None, 2, 3],
+        "xysa": [0, 1, None, 3, 2],
+        "xyz": [0, 1, 2, None, None],
+    }
+
+    indices: list[int | None] = format_to_indices[source_format]
+
+    processed_keypoints = np.zeros((keypoints.shape[0], NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS), dtype=np.float32)
+
+    for i, idx in enumerate(indices):
+        if idx is not None:
+            processed_keypoints[:, i] = keypoints[:, idx]
 
-    processed_keypoints[:, 2] = angle_to_2pi_range(processed_keypoints[:, 2])
-
-    if keypoints.shape[1] > len(source_format):
-        processed_keypoints = np.column_stack((processed_keypoints, keypoints[:, len(source_format) :]))
+    if angle_in_degrees and indices[3] is not None:  # angle is now at index 3
+        processed_keypoints[:, 3] = np.radians(processed_keypoints[:, 3])
+
+    processed_keypoints[:, 3] = angle_to_2pi_range(processed_keypoints[:, 3])  # angle is now at index 3
 
-    if check_validity:
-        check_keypoints(processed_keypoints, image_shape)
+    if keypoints.shape[1] > len(source_format):
+        processed_keypoints = np.column_stack((processed_keypoints, keypoints[:, len(source_format) :]))
 
-    return processed_keypoints
-

def filter_keypoints (keypoints, image_shape, remove_invisible) [view source on GitHub]¶

Filter keypoints to remove those outside the image boundaries.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint (x, y, ...).

image_shape tuple[int, int]

A tuple (height, width) representing the image dimensions.

remove_invisible bool

If True, remove keypoints outside the image boundaries.

Returns:

Type Description
np.ndarray

A numpy array of filtered keypoints.

Source code in albumentations/core/keypoints_utils.py
Python
def filter_keypoints(
+    if check_validity:
+        check_keypoints(processed_keypoints, shape)
+
+    return processed_keypoints
+

def filter_keypoints (keypoints, shape, remove_invisible) [view source on GitHub]¶

Filter keypoints to remove those outside the boundaries.

Parameters:

Name Type Description
keypoints np.ndarray

A numpy array of shape (N, 5+) where N is the number of keypoints. Each row represents a keypoint (x, y, z, angle, scale, ...).

shape ShapeType

Shape to check against as {'height': height, 'width': width, 'depth': depth}.

remove_invisible bool

If True, remove keypoints outside the boundaries.

Returns:

Type Description
np.ndarray

A numpy array of filtered keypoints.

Source code in albumentations/core/keypoints_utils.py
Python
def filter_keypoints(
     keypoints: np.ndarray,
-    image_shape: tuple[int, int],
+    shape: ShapeType,
     remove_invisible: bool,
 ) -> np.ndarray:
-    """Filter keypoints to remove those outside the image boundaries.
+    """Filter keypoints to remove those outside the boundaries.
 
     Args:
-        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.
-                   Each row represents a keypoint (x, y, ...).
-        image_shape: A tuple (height, width) representing the image dimensions.
-        remove_invisible: If True, remove keypoints outside the image boundaries.
+        keypoints: A numpy array of shape (N, 5+) where N is the number of keypoints.
+                   Each row represents a keypoint (x, y, z, angle, scale, ...).
+        shape: Shape to check against as {'height': height, 'width': width, 'depth': depth}.
+        remove_invisible: If True, remove keypoints outside the boundaries.
 
     Returns:
         A numpy array of filtered keypoints.
@@ -288,12 +378,15 @@
     if not keypoints.size:
         return keypoints
 
-    height, width = image_shape[:2]
+    height, width, depth = shape["height"], shape["width"], shape.get("depth", None)
 
     # Create boolean mask for visible keypoints
-    x, y = keypoints[:, 0], keypoints[:, 1]
+    x, y, z = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2]
     visible = (x >= 0) & (x < width) & (y >= 0) & (y < height)
 
-    # Apply the mask to filter keypoints
-    return keypoints[visible]
+    if depth is not None:
+        visible &= (z >= 0) & (z < depth)
+
+    # Apply the mask to filter keypoints
+    return keypoints[visible]
 
\ No newline at end of file diff --git a/docs/api_reference/core/transforms_interface/index.html b/docs/api_reference/core/transforms_interface/index.html index 45f65179..2114b79e 100644 --- a/docs/api_reference/core/transforms_interface/index.html +++ b/docs/api_reference/core/transforms_interface/index.html @@ -589,12 +589,18 @@ def apply_to_volume(self, volume: np.ndarray, **params: Any) -> np.ndarray: return volume - def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> np.ndarray: - return mask3d + def apply_to_volumes(self, volumes: np.ndarray, **params: Any) -> np.ndarray: + return volumes - def get_transform_init_args_names(self) -> tuple[str, ...]: - return () -

class Transform3D [view source on GitHub] ¶

Base class for all 3D transforms.

Transform3D inherits from DualTransform because 3D transforms can be applied to both volumes and masks, similar to how 2D DualTransforms work with images and masks.

Targets

volume: 3D numpy array of shape (D, H, W) or (D, H, W, C) volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C) mask: 3D numpy array of shape (D, H, W) masks: Batch of 3D arrays of shape (N, D, H, W)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py
Python
class Transform3D(DualTransform):
+    def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> np.ndarray:
+        return mask3d
+
+    def apply_to_masks3d(self, masks3d: np.ndarray, **params: Any) -> np.ndarray:
+        return masks3d
+
+    def get_transform_init_args_names(self) -> tuple[str, ...]:
+        return ()
+

class Transform3D [view source on GitHub] ¶

Base class for all 3D transforms.

Transform3D inherits from DualTransform because 3D transforms can be applied to both volumes and masks, similar to how 2D DualTransforms work with images and masks.

Targets

volume: 3D numpy array of shape (D, H, W) or (D, H, W, C) volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C) mask: 3D numpy array of shape (D, H, W) masks: Batch of 3D arrays of shape (N, D, H, W) keypoints: 3D numpy array of shape (N, 3)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py
Python
class Transform3D(DualTransform):
     """Base class for all 3D transforms.
 
     Transform3D inherits from DualTransform because 3D transforms can be applied to both
@@ -605,33 +611,35 @@
         volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C)
         mask: 3D numpy array of shape (D, H, W)
         masks: Batch of 3D arrays of shape (N, D, H, W)
-    """
-
-    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
-        """Apply transform to single 3D volume."""
-        raise NotImplementedError
-
-    @batch_transform("spatial", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)
-    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
-        """Apply transform to batch of 3D volumes."""
-        return self.apply_to_volume(volumes, *args, **params)
-
-    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
-        """Apply transform to single 3D mask."""
-        return self.apply_to_volume(mask3d, *args, **params)
-
-    @batch_transform("spatial", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)
-    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
-        """Apply transform to batch of 3D masks."""
-        return self.apply_to_mask3d(masks3d, *args, **params)
-
-    @property
-    def targets(self) -> dict[str, Callable[..., Any]]:
-        """Define valid targets for 3D transforms."""
-        return {
-            "volume": self.apply_to_volume,
-            "volumes": self.apply_to_volumes,
-            "mask3d": self.apply_to_mask3d,
-            "masks3d": self.apply_to_masks3d,
-        }
+        keypoints: 3D numpy array of shape (N, 3)
+    """
+
+    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
+        """Apply transform to single 3D volume."""
+        raise NotImplementedError
+
+    @batch_transform("spatial", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)
+    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
+        """Apply transform to batch of 3D volumes."""
+        return self.apply_to_volume(volumes, *args, **params)
+
+    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
+        """Apply transform to single 3D mask."""
+        return self.apply_to_volume(mask3d, *args, **params)
+
+    @batch_transform("spatial", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)
+    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:
+        """Apply transform to batch of 3D masks."""
+        return self.apply_to_mask3d(masks3d, *args, **params)
+
+    @property
+    def targets(self) -> dict[str, Callable[..., Any]]:
+        """Define valid targets for 3D transforms."""
+        return {
+            "volume": self.apply_to_volume,
+            "volumes": self.apply_to_volumes,
+            "mask3d": self.apply_to_mask3d,
+            "masks3d": self.apply_to_masks3d,
+            "keypoints": self.apply_to_keypoints,
+        }
 
\ No newline at end of file diff --git a/docs/api_reference/full_reference/index.html b/docs/api_reference/full_reference/index.html index 13d0a834..6eb4e1b3 100644 --- a/docs/api_reference/full_reference/index.html +++ b/docs/api_reference/full_reference/index.html @@ -6,4 +6,4 @@ .jupyter-wrapper .jp-MarkdownOutput.jp-RenderedHTMLCommon { font-size: 0.8rem; } -

Full API Reference on a single page

Transform Types

1. Pixel-level transforms

Transforms that modify pixel values without changing spatial relationships. These can be safely applied to any target as they only affect the input image, leaving other targets (masks, bounding boxes, keypoints) unchanged.

2. Spatial-level transforms

Transforms that modify the spatial arrangement of pixels/features. Different targets have different spatial transform support - see the compatibility table below:

Transform Image Mask BBoxes Keypoints Volume Mask3D
Affine ✓ ✓ ✓ ✓ ✓ ✓
AtLeastOneBBoxRandomCrop ✓ ✓ ✓ ✓ ✓ ✓
BBoxSafeRandomCrop ✓ ✓ ✓ ✓ ✓ ✓
CenterCrop ✓ ✓ ✓ ✓ ✓ ✓
CoarseDropout ✓ ✓ ✓ ✓ ✓ ✓
Crop ✓ ✓ ✓ ✓ ✓ ✓
CropAndPad ✓ ✓ ✓ ✓ ✓ ✓
CropNonEmptyMaskIfExists ✓ ✓ ✓ ✓ ✓ ✓
D4 ✓ ✓ ✓ ✓ ✓ ✓
ElasticTransform ✓ ✓ ✓ ✓ ✓ ✓
Erasing ✓ ✓ ✓ ✓ ✓ ✓
FrequencyMasking ✓ ✓ ✓ ✓ ✓ ✓
GridDistortion ✓ ✓ ✓ ✓ ✓ ✓
GridDropout ✓ ✓ ✓ ✓ ✓ ✓
GridElasticDeform ✓ ✓ ✓ ✓ ✓ ✓
HorizontalFlip ✓ ✓ ✓ ✓ ✓ ✓
Lambda ✓ ✓ ✓ ✓ ✓ ✓
LongestMaxSize ✓ ✓ ✓ ✓ ✓ ✓
MaskDropout ✓ ✓ ✓ ✓ ✓ ✓
Morphological ✓ ✓ ✓ ✓ ✓ ✓
NoOp ✓ ✓ ✓ ✓ ✓ ✓
OpticalDistortion ✓ ✓ ✓ ✓ ✓ ✓
OverlayElements ✓ ✓
Pad ✓ ✓ ✓ ✓ ✓ ✓
PadIfNeeded ✓ ✓ ✓ ✓ ✓ ✓
Perspective ✓ ✓ ✓ ✓ ✓ ✓
PiecewiseAffine ✓ ✓ ✓ ✓ ✓ ✓
PixelDropout ✓ ✓ ✓ ✓ ✓ ✓
RandomCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomCropFromBorders ✓ ✓ ✓ ✓ ✓ ✓
RandomCropNearBBox ✓ ✓ ✓ ✓ ✓ ✓
RandomGridShuffle ✓ ✓ ✓ ✓ ✓ ✓
RandomResizedCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomRotate90 ✓ ✓ ✓ ✓ ✓ ✓
RandomScale ✓ ✓ ✓ ✓ ✓ ✓
RandomSizedBBoxSafeCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomSizedCrop ✓ ✓ ✓ ✓ ✓ ✓
Resize ✓ ✓ ✓ ✓ ✓ ✓
Rotate ✓ ✓ ✓ ✓ ✓ ✓
SafeRotate ✓ ✓ ✓ ✓ ✓ ✓
ShiftScaleRotate ✓ ✓ ✓ ✓ ✓ ✓
SmallestMaxSize ✓ ✓ ✓ ✓ ✓ ✓
ThinPlateSpline ✓ ✓ ✓ ✓ ✓ ✓
TimeMasking ✓ ✓ ✓ ✓ ✓ ✓
TimeReverse ✓ ✓ ✓ ✓ ✓ ✓
Transpose ✓ ✓ ✓ ✓ ✓ ✓
VerticalFlip ✓ ✓ ✓ ✓ ✓ ✓
XYMasking ✓ ✓ ✓ ✓ ✓ ✓

3. Volumetric (3D) transforms

Transforms designed for three-dimensional data (D, H, W). These operate on volumes and their corresponding 3D masks, supporting both single-channel and multi-channel data.

Transform Image Mask BBoxes Keypoints Volume Mask3D
CenterCrop3D ✓ ✓
CoarseDropout3D ✓ ✓
CubicSymmetry ✓ ✓
Pad3D ✓ ✓
PadIfNeeded3D ✓ ✓
RandomCrop3D ✓ ✓
\ No newline at end of file +

Full API Reference on a single page

Transform Types

1. Pixel-level transforms

Transforms that modify pixel values without changing spatial relationships. These can be safely applied to any target as they only affect the input image, leaving other targets (masks, bounding boxes, keypoints) unchanged.

2. Spatial-level transforms

Transforms that modify the spatial arrangement of pixels/features. Different targets have different spatial transform support - see the compatibility table below:

Transform Image Mask BBoxes Keypoints Volume Mask3D
Affine ✓ ✓ ✓ ✓ ✓ ✓
AtLeastOneBBoxRandomCrop ✓ ✓ ✓ ✓ ✓ ✓
BBoxSafeRandomCrop ✓ ✓ ✓ ✓ ✓ ✓
CenterCrop ✓ ✓ ✓ ✓ ✓ ✓
CoarseDropout ✓ ✓ ✓ ✓ ✓ ✓
Crop ✓ ✓ ✓ ✓ ✓ ✓
CropAndPad ✓ ✓ ✓ ✓ ✓ ✓
CropNonEmptyMaskIfExists ✓ ✓ ✓ ✓ ✓ ✓
D4 ✓ ✓ ✓ ✓ ✓ ✓
ElasticTransform ✓ ✓ ✓ ✓ ✓ ✓
Erasing ✓ ✓ ✓ ✓ ✓ ✓
FrequencyMasking ✓ ✓ ✓ ✓ ✓ ✓
GridDistortion ✓ ✓ ✓ ✓ ✓ ✓
GridDropout ✓ ✓ ✓ ✓ ✓ ✓
GridElasticDeform ✓ ✓ ✓ ✓ ✓ ✓
HorizontalFlip ✓ ✓ ✓ ✓ ✓ ✓
Lambda ✓ ✓ ✓ ✓ ✓ ✓
LongestMaxSize ✓ ✓ ✓ ✓ ✓ ✓
MaskDropout ✓ ✓ ✓ ✓ ✓ ✓
Morphological ✓ ✓ ✓ ✓ ✓ ✓
NoOp ✓ ✓ ✓ ✓ ✓ ✓
OpticalDistortion ✓ ✓ ✓ ✓ ✓ ✓
OverlayElements ✓ ✓
Pad ✓ ✓ ✓ ✓ ✓ ✓
PadIfNeeded ✓ ✓ ✓ ✓ ✓ ✓
Perspective ✓ ✓ ✓ ✓ ✓ ✓
PiecewiseAffine ✓ ✓ ✓ ✓ ✓ ✓
PixelDropout ✓ ✓ ✓ ✓ ✓ ✓
RandomCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomCropFromBorders ✓ ✓ ✓ ✓ ✓ ✓
RandomCropNearBBox ✓ ✓ ✓ ✓ ✓ ✓
RandomGridShuffle ✓ ✓ ✓ ✓ ✓ ✓
RandomResizedCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomRotate90 ✓ ✓ ✓ ✓ ✓ ✓
RandomScale ✓ ✓ ✓ ✓ ✓ ✓
RandomSizedBBoxSafeCrop ✓ ✓ ✓ ✓ ✓ ✓
RandomSizedCrop ✓ ✓ ✓ ✓ ✓ ✓
Resize ✓ ✓ ✓ ✓ ✓ ✓
Rotate ✓ ✓ ✓ ✓ ✓ ✓
SafeRotate ✓ ✓ ✓ ✓ ✓ ✓
ShiftScaleRotate ✓ ✓ ✓ ✓ ✓ ✓
SmallestMaxSize ✓ ✓ ✓ ✓ ✓ ✓
ThinPlateSpline ✓ ✓ ✓ ✓ ✓ ✓
TimeMasking ✓ ✓ ✓ ✓ ✓ ✓
TimeReverse ✓ ✓ ✓ ✓ ✓ ✓
Transpose ✓ ✓ ✓ ✓ ✓ ✓
VerticalFlip ✓ ✓ ✓ ✓ ✓ ✓
XYMasking ✓ ✓ ✓ ✓ ✓ ✓

3. Volumetric (3D) transforms

Transforms designed for three-dimensional data (D, H, W). These operate on volumes and their corresponding 3D masks, supporting both single-channel and multi-channel data.

Transform Image Mask BBoxes Keypoints Volume Mask3D
CenterCrop3D ✓ ✓ ✓
CoarseDropout3D ✓ ✓ ✓
CubicSymmetry ✓ ✓ ✓
Pad3D ✓ ✓ ✓
PadIfNeeded3D ✓ ✓ ✓
RandomCrop3D ✓ ✓ ✓
\ No newline at end of file diff --git a/docs/search/search_index.json b/docs/search/search_index.json index dc6aedd5..7a3f2e56 100644 --- a/docs/search/search_index.json +++ b/docs/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Albumentations documentation","text":"

Albumentations is a fast and flexible image augmentation library. The library is widely used in industry, deep learning research, machine learning competitions, and open source projects. Albumentations is written in Python, and it is licensed under the MIT license. The source code is available at https://github.com/albumentations-team/albumentations.

If you are new to image augmentation, start with our \"Learning Path\" for beginners. It describes what image augmentation is, how it can boost deep neural networks' performance, and why you should use Albumentations.

For hands-on experience, check out our \"Quick Start Guide\" and \"Examples\" sections. They show how you can use the library for different computer vision tasks: image classification, semantic segmentation, instance segmentation, object detection, and keypoint detection. Each example includes a link to Google Colab, where you can run the code by yourself.

You can also visit explore.albumentations.ai to visually explore and experiment with different augmentations in your browser. This interactive tool helps you better understand how each transform affects images before implementing it in your code.

\"API Reference\" contains the description of Albumentations' methods and classes.

"},{"location":"#quick-start-guide","title":"Quick Start Guide","text":"
  • Installation
  • Frequently Asked Questions
  • Your First Augmentation Pipeline
"},{"location":"#working-with-multi-dimensional-data","title":"Working with Multi-dimensional Data","text":""},{"location":"#volumetric-data-3d","title":"Volumetric Data (3D)","text":"
  • Introduction to 3D (Volumetric) Image Augmentation
  • Available 3D Transforms
"},{"location":"#video-and-sequential-data","title":"Video and Sequential Data","text":"
  • Video Frame Augmentation
"},{"location":"#learning-path","title":"Learning Path","text":""},{"location":"#beginners","title":"Beginners","text":"
  • What is Image Augmentation?
  • Why Choose Albumentations?
  • Basic Image Classification
"},{"location":"#intermediate","title":"Intermediate","text":"
  • Semantic Segmentation
  • Object Detection
  • Keypoint Detection
  • Multi-target Augmentation
"},{"location":"#advanced","title":"Advanced","text":"
  • Pipeline Configuration
  • Debugging with ReplayCompose
  • Serialization
"},{"location":"#framework-integration","title":"Framework Integration","text":"
  • PyTorch
  • TensorFlow
  • HuggingFace
  • Roboflow
  • Voxel51
"},{"location":"#library-comparisons","title":"Library Comparisons","text":"
  • Transform Library Comparison - Find equivalent transforms between Albumentations and other libraries (torchvision, Kornia)
  • Migration from torchvision - Step-by-step migration guide
"},{"location":"#examples","title":"Examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Using Albumentations for a semantic segmentation task
  • Using Albumentations to augment keypoints
  • Applying the same augmentation with the same parameters to multiple images, masks, bounding boxes, or keypoints
  • Weather augmentations in Albumentations
  • Example of applying XYMasking transform
  • Example of applying ChromaticAberration transform
  • Example of applying Morphological transform
  • Example of applying D4 transform
  • Example of applying RandomGridShuffle transform
  • Example of applying OverlayElements transform
  • Example of applying TextImage transform
  • Debugging an augmentation pipeline with ReplayCompose
  • How to save and load parameters of an augmentation pipeline
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
  • How to save and load transforms to HuggingFace Hub.
"},{"location":"#examples-of-how-to-use-albumentations-with-different-deep-learning-frameworks","title":"Examples of how to use Albumentations with different deep learning frameworks","text":"
  • PyTorch and Albumentations for image classification
  • PyTorch and Albumentations for semantic segmentation
  • Using Albumentations with Tensorflow
"},{"location":"#external-resources","title":"External resources","text":"
  • Blog posts, podcasts, talks, and videos about Albumentations
  • Books that mention Albumentations
  • Online courses that cover Albumentations
"},{"location":"#other-topics","title":"Other topics","text":"
  • Contributing
"},{"location":"#api-reference","title":"API Reference","text":"
  • Full API Reference on a single page
  • Index
  • Core API (albumentations.core)
  • Augmentations (albumentations.augmentations)
  • PyTorch Helpers (albumentations.pytorch)
"},{"location":"CONTRIBUTING/","title":"Contributing to Albumentations","text":"

Thank you for your interest in contributing to Albumentations! This guide will help you get started with contributing to our image augmentation library.

"},{"location":"CONTRIBUTING/#quick-start","title":"Quick Start","text":"

For small changes (e.g., bug fixes), feel free to submit a PR directly.

For larger changes:

  1. Create an issue outlining your proposed change
  2. Join our Discord community to discuss your idea
"},{"location":"CONTRIBUTING/#contribution-guides","title":"Contribution Guides","text":"

We've organized our contribution guidelines into focused documents:

  • Environment Setup Guide - How to set up your development environment
  • Coding Guidelines - Code style, best practices, and technical requirements
"},{"location":"CONTRIBUTING/#contribution-process","title":"Contribution Process","text":"
  1. Find an Issue: Look for open issues or propose a new one. For newcomers, look for issues labeled \"good first issue\"
  2. Set Up: Follow our Environment Setup Guide
  3. Create a Branch: git checkout -b feature/my-new-feature
  4. Make Changes: Write code following our Coding Guidelines
  5. Test: Add tests and ensure all tests pass
  6. Submit: Open a Pull Request with a clear description of your changes
"},{"location":"CONTRIBUTING/#code-review-process","title":"Code Review Process","text":"
  1. Maintainers will review your contribution
  2. Address any feedback or questions
  3. Once approved, your code will be merged
"},{"location":"CONTRIBUTING/#project-structure","title":"Project Structure","text":"
  • albumentations/ - Main source code
  • tests/ - Test suite
  • docs/ - Documentation
"},{"location":"CONTRIBUTING/#getting-help","title":"Getting Help","text":"
  • Join our Discord community
  • Open a GitHub issue
  • Ask questions in your pull request
"},{"location":"CONTRIBUTING/#license","title":"License","text":"

By contributing, you agree that your contributions will be licensed under the project's MIT License.

"},{"location":"benchmarking_results/","title":"Benchmarking results","text":""},{"location":"benchmarking_results/#benchmarking-results_1","title":"Benchmarking results","text":""},{"location":"benchmarking_results/#system-information","title":"System Information","text":"
  • Platform: macOS-15.0.1-arm64-arm-64bit
  • Processor: arm
  • CPU Count: 10
  • Python Version: 3.12.7
"},{"location":"benchmarking_results/#benchmark-parameters","title":"Benchmark Parameters","text":"
  • Number of images: 1000
  • Runs per transform: 10
  • Max warmup iterations: 1000
"},{"location":"benchmarking_results/#library-versions","title":"Library Versions","text":"
  • albumentations: 1.4.20
  • augly: 1.0.0
  • imgaug: 0.4.0
  • kornia: 0.7.3
  • torchvision: 0.20.0
"},{"location":"benchmarking_results/#performance-comparison","title":"Performance Comparison","text":"

Number - is the number of uint8 RGB images processed per second on a single CPU core. Higher is better.

Transform albumentations1.4.20 augly1.0.0 imgaug0.4.0 kornia0.7.3 torchvision0.20.0 HorizontalFlip 8618 \u00b1 1233 4807 \u00b1 818 6042 \u00b1 788 390 \u00b1 106 914 \u00b1 67 VerticalFlip 22847 \u00b1 2031 9153 \u00b1 1291 10931 \u00b1 1844 1212 \u00b1 402 3198 \u00b1 200 Rotate 1146 \u00b1 79 1119 \u00b1 41 1136 \u00b1 218 143 \u00b1 11 181 \u00b1 11 Affine 682 \u00b1 192 - 774 \u00b1 97 147 \u00b1 9 130 \u00b1 12 Equalize 892 \u00b1 61 - 581 \u00b1 54 152 \u00b1 19 479 \u00b1 12 RandomCrop80 47341 \u00b1 20523 25272 \u00b1 1822 11503 \u00b1 441 1510 \u00b1 230 32109 \u00b1 1241 ShiftRGB 2349 \u00b1 76 - 1582 \u00b1 65 - - Resize 2316 \u00b1 166 611 \u00b1 78 1806 \u00b1 63 232 \u00b1 24 195 \u00b1 4 RandomGamma 8675 \u00b1 274 - 2318 \u00b1 269 108 \u00b1 13 - Grayscale 3056 \u00b1 47 2720 \u00b1 932 1681 \u00b1 156 289 \u00b1 75 1838 \u00b1 130 RandomPerspective 412 \u00b1 38 - 554 \u00b1 22 86 \u00b1 11 96 \u00b1 5 GaussianBlur 1728 \u00b1 89 242 \u00b1 4 1090 \u00b1 65 176 \u00b1 18 79 \u00b1 3 MedianBlur 868 \u00b1 60 - 813 \u00b1 30 5 \u00b1 0 - MotionBlur 4047 \u00b1 67 - 612 \u00b1 18 73 \u00b1 2 - Posterize 9094 \u00b1 301 - 2097 \u00b1 68 430 \u00b1 49 3196 \u00b1 185 JpegCompression 918 \u00b1 23 778 \u00b1 5 459 \u00b1 35 71 \u00b1 3 625 \u00b1 17 GaussianNoise 166 \u00b1 12 67 \u00b1 2 206 \u00b1 11 75 \u00b1 1 - Elastic 201 \u00b1 5 - 235 \u00b1 20 1 \u00b1 0 2 \u00b1 0 Clahe 454 \u00b1 22 - 335 \u00b1 43 94 \u00b1 9 - CoarseDropout 13368 \u00b1 744 - 671 \u00b1 38 536 \u00b1 87 - Blur 5267 \u00b1 543 246 \u00b1 3 3807 \u00b1 325 - - ColorJitter 628 \u00b1 55 255 \u00b1 13 - 55 \u00b1 18 46 \u00b1 2 Brightness 8956 \u00b1 300 1163 \u00b1 86 - 472 \u00b1 101 429 \u00b1 20 Contrast 8879 \u00b1 1426 736 \u00b1 79 - 425 \u00b1 52 335 \u00b1 35 RandomResizedCrop 2828 \u00b1 186 - - 287 \u00b1 58 511 \u00b1 10 Normalize 1196 \u00b1 56 - - 626 \u00b1 40 519 \u00b1 12 PlankianJitter 2204 \u00b1 385 - - 813 \u00b1 211 -"},{"location":"faq/","title":"Frequently Asked Questions","text":"

This FAQ covers common questions about Albumentations, from basic setup to advanced usage. You'll find information about:

  • Installation troubleshooting and configuration
  • Working with different data formats (images, video, volumetric data)
  • Advanced usage patterns and best practices
  • Integration with other tools and migration from other libraries

If you don't find an answer to your question, please check our GitHub Issues or join our Discord community.

"},{"location":"faq/#installation","title":"Installation","text":""},{"location":"faq/#i-am-receiving-an-error-message-failed-building-wheel-for-imagecodecs-when-i-am-trying-to-install-albumentations-how-can-i-fix-the-problem","title":"I am receiving an error message Failed building wheel for imagecodecs when I am trying to install Albumentations. How can I fix the problem?","text":"

Try to update pip by running the following command:

Bash
python -m pip install --upgrade pip\n
"},{"location":"faq/#how-to-disable-automatic-checks-for-new-versions","title":"How to disable automatic checks for new versions?","text":"

To disable automatic checks for new versions, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.

"},{"location":"faq/#how-to-make-albumentations-use-one-cpu-core","title":"How to make Albumentations use one CPU core?","text":"

Albumentations do not use multithreading by default, but libraries it depends on (like opencv) may use multithreading. To make Albumentations use one CPU core, you can set the following environment variables:

Python
os.environ[\"OMP_NUM_THREADS\"] = \"1\"\nos.environ[\"OPENBLAS_NUM_THREADS\"] = \"1\"\nos.environ[\"MKL_NUM_THREADS\"] = \"1\"\nos.environ[\"VECLIB_MAXIMUM_THREADS\"] = \"1\"\nos.environ[\"NUMEXPR_NUM_THREADS\"] = \"1\"\n
"},{"location":"faq/#data-formats-and-basic-usage","title":"Data Formats and Basic Usage","text":""},{"location":"faq/#supported-image-types","title":"Supported Image Types","text":"

Albumentations works with images of type uint8 and float32. uint8 images should be in the [0, 255] range, and float32 images should be in the [0, 1] range. If float32 images lie outside of the [0, 1] range, they will be automatically clipped to the [0, 1] range.

"},{"location":"faq/#why-do-you-call-cv2cvtcolorimage-cv2color_bgr2rgb-in-your-examples","title":"Why do you call cv2.cvtColor(image, cv2.COLOR_BGR2RGB) in your examples?","text":"

For historical reasons, OpenCV reads an image in BGR format (so color channels of the image have the following order: Blue, Green, Red). Albumentations uses the most common and popular RGB image format. So when using OpenCV, we need to convert the image format to RGB explicitly.

"},{"location":"faq/#how-to-have-reproducible-augmentations","title":"How to have reproducible augmentations?","text":"

To have reproducible augmentations, set the seed parameter in your transform pipeline. This will ensure that the same random parameters are used for each augmentation, resulting in the same output for the same input.

Python
transform = A.Compose([\n    A.RandomCrop(height=256, width=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], seed=42)\n
"},{"location":"faq/#working-with-different-data-types","title":"Working with Different Data Types","text":""},{"location":"faq/#how-to-process-video-data-with-albumentations","title":"How to process video data with Albumentations?","text":"

Albumentations can process video data by treating it as a sequence of frames in numpy array format: - (N, H, W) - Grayscale video (N frames) - (N, H, W, C) - Color video (N frames)

When you pass a video array, Albumentations will apply the same transform with identical parameters to each frame, ensuring temporal consistency.

Python
video = np.random.rand(32, 256, 256, 3) # 32 RGB frames\n\ntransform = A.Compose([\n  A.RandomCrop(height=224, width=224),\n  A.HorizontalFlip(p=0.5)\n], seed=42)\n\ntransformed = transform(image=video)['image']\n

See Working with Video Data for more info.

"},{"location":"faq/#how-to-process-volumetric-data-with-albumentations","title":"How to process volumetric data with Albumentations?","text":"

Albumentations can process volumetric data by treating it as a sequence of 2D slices. When you pass a volumetric data as a numpy array, Albumentations will apply the same transform with identical parameters to each slice, ensuring temporal consistency.

See Working with Volumetric Data (3D) for more info.

"},{"location":"faq/#my-computer-vision-pipeline-works-with-a-sequence-of-images-i-want-to-apply-the-same-augmentations-with-the-same-parameters-to-each-image-in-the-sequence-can-albumentations-do-it","title":"My computer vision pipeline works with a sequence of images. I want to apply the same augmentations with the same parameters to each image in the sequence. Can Albumentations do it?","text":"

Yes. You can define additional images, masks, bounding boxes, or keypoints through the additional_targets argument to Compose. You can then pass those additional targets to the augmentation pipeline, and Albumentations will augment them in the same way. See this example for more info.

But if you want only to the sequence of images, you may just use images target that accepts list[numpy.ndarray] or np.ndarray with shape (N, H, W, C) / (N, H, W).

"},{"location":"faq/#advanced-usage","title":"Advanced Usage","text":""},{"location":"faq/#how-can-i-find-which-augmentations-were-applied-to-the-input-data-and-which-parameters-they-used","title":"How can I find which augmentations were applied to the input data and which parameters they used?","text":"

You may pass save_applied_params=True to Compose to save the parameters of the applied augmentations. You can access them later using applied_transforms.

Python
transform = A.Compose([\n    A.RandomCrop(256, 256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.5),\n    A.RandomGamma(p=0.5),\n    A.Normalize(),\n], save_applied_params=True, seed=42)\n\ntransformed = transform(image=image)['image']\n\nprint(transform[\"applied_transforms\"])\n
"},{"location":"faq/#how-to-perform-balanced-scaling","title":"How to perform balanced scaling?","text":"

The default scaling logic in RandomScale, ShiftScaleRotate, and Affine transformations is biased towards upscaling.

For example, if scale_limit = (0.5, 2), a user might expect that the image will be scaled down in half of the cases and scaled up in the other half. However, in reality, the image will be scaled up in 75% of the cases and scaled down in only 25% of the cases. This is because the default behavior samples uniformly from the interval [0.5, 2], and the interval [0.5, 1] is three times smaller than [1, 2].

To achieve balanced scaling, you can use Affine with balanced_scale=True, which ensures that the probability of scaling up and scaling down is equal.

Python
balanced_scale_transform = A.Affine(scale=(0.5, 2), balanced_scale=True)\n

or use OneOf transform as follows:

Python
balanced_scale_transform = A.OneOf([\n  A.Affine(scale=(0.5, 1), p=0.5),\n  A.Affine(scale=(1, 2), p=0.5)])\n

This approach ensures that exactly half of the samples will be upscaled and half will be downscaled.

"},{"location":"faq/#augmentations-have-a-parameter-named-p-that-sets-the-probability-of-applying-that-augmentation-how-does-p-work-in-nested-containers","title":"Augmentations have a parameter named p that sets the probability of applying that augmentation. How does p work in nested containers?","text":"

The p parameter sets the probability of applying a specific augmentation. When augmentations are nested within a top-level container like Compose, the effective probability of each augmentation is the product of the container's probability and the augmentation's probability.

Let's look at an example when a container Compose contains one augmentation Resize:

Python
transform = A.Compose([\n    A.Resize(height=256, width=256, p=1.0),\n], p=0.9)\n

In this case, Resize has a 90% chance to be applied. This is because there is a 90% chance for Compose to be applied (p=0.9). If Compose is applied, then Resize is applied with 100% probability (p=1.0).

To visualize:

  • Probability of Compose being applied: 0.9
  • Probability of Resize being applied given Compose is applied: 1.0
  • Effective probability of Resize being applied: 0.9 * 1.0 = 0.9 (or 90%)

This means that the effective probability of Resize being applied is the product of the probabilities of Compose and Resize, which is 0.9 * 1.0 = 0.9 or 90%. This principle applies to other transformations as well, where the overall probability is the product of the individual probabilities within the transformation pipeline.

Here\u2019s another example:

Python
transform = A.Compose([\n    A.Resize(height=256, width=256, p=0.5),\n], p=0.9)\n

In this example, Resize has an effective probability of being applied as 0.9 * 0.5 = 0.45 or 45%. This is because Compose is applied 90% of the time, and within that 90%, Resize is applied 50% of the time.

"},{"location":"faq/#i-created-annotations-for-bounding-boxes-using-labeling-service-or-labeling-software-how-can-i-use-those-annotations-in-albumentations","title":"I created annotations for bounding boxes using labeling service or labeling software. How can I use those annotations in Albumentations?","text":"

You need to convert those annotations to one of the formats, supported by Albumentations. For the list of formats, please refer to this article. Consult the documentation of the labeling service to see how you can export annotations in those formats.

"},{"location":"faq/#integration-and-migration","title":"Integration and Migration","text":""},{"location":"faq/#how-to-save-and-load-augmentation-transforms-to-huggingface-hub","title":"How to save and load augmentation transforms to HuggingFace Hub?","text":"Python
import albumentations as A\nimport numpy as np\n\ntransform = A.Compose([\n    A.RandomCrop(256, 256),\n    A.HorizontalFlip(),\n    A.RandomBrightnessContrast(),\n    A.RGBShift(),\n    A.Normalize(),\n])\n\ntransform.save_pretrained(\"qubvel-hf/albu\", key=\"train\")\n# The 'key' parameter specifies the context or purpose of the saved transform,\n# allowing for organized and context-specific retrieval.\n# ^ this will save the transform to a directory \"qubvel-hf/albu\" with filename \"albumentations_config_train.json\"\n\ntransform.save_pretrained(\"qubvel-hf/albu\", key=\"train\", push_to_hub=True)\n# ^ this will save the transform to a directory \"qubvel-hf/albu\" with filename \"albumentations_config_train.json\"\n# + push the transform to the Hub to the repository \"qubvel-hf/albu\"\n\ntransform.push_to_hub(\"qubvel-hf/albu\", key=\"train\")\n# Use `save_pretrained` to save the transform locally and optionally push to the Hub.\n# Use `push_to_hub` to directly push the transform to the Hub without saving it locally.\n# ^ this will push the transform to the Hub to the repository \"qubvel-hf/albu\" (without saving it locally)\n\nloaded_transform = A.Compose.from_pretrained(\"qubvel-hf/albu\", key=\"train\")\n# ^ this will load the transform from local folder if exist or from the Hub repository \"qubvel-hf/albu\"\n

See this example for more info.

"},{"location":"faq/#how-do-i-migrate-from-other-augmentation-libraries-to-albumentations","title":"How do I migrate from other augmentation libraries to Albumentations?","text":"

If you're migrating from other libraries like torchvision or Kornia, you can refer to our Library Comparison & Benchmarks guide. This guide provides:

  1. Mapping tables showing equivalent transforms between libraries
  2. Performance benchmarks demonstrating Albumentations' speed advantages
  3. Code examples for common migration scenarios
  4. Key differences in implementation and parameter handling

For a quick visual comparison of different augmentations, you can also use our interactive tool at explore.albumentations.ai to see how transforms affect images before implementing them.

For specific migration examples, see:

  • Migrating from torchvision
  • Performance comparison with other libraries
"},{"location":"frameworks_and_libraries/","title":"Frameworks and libraries that use Albumentations","text":""},{"location":"frameworks_and_libraries/#mmdetection","title":"MMDetection","text":"

https://github.com/open-mmlab/mmdetection

MMDetection is an open source object detection toolbox based on PyTorch. It is a part of the OpenMMLab project.

  • To install MMDetection with Albumentations follow the installation instructions.
  • MMDetection has an example config with augmentations from Albumentations.
"},{"location":"frameworks_and_libraries/#yolov5","title":"YOLOv5","text":"

https://github.com/ultralytics/yolov5

YOLOv5 \ud83d\ude80 is a family of object detection architectures and models pretrained on the COCO dataset, and represents Ultralytics open-source research into future vision AI methods, incorporating lessons learned and best practices evolved over thousands of hours of research and development.

  • To use Albumentations along with YOLOv5 simply pip install -U albumentations and then update the augmentation pipeline as you see fit in the Albumentations class in utils/augmentations.py. An example is available in the YOLOv5 repository.
"},{"location":"frameworks_and_libraries/#other-frameworks-and-libraries","title":"Other frameworks and libraries","text":"

Other you can see find at GitHub

"},{"location":"api_reference/","title":"Index","text":"
  • Full API Reference on a single page
  • Core API (albumentations.core)
    • Composition API (albumentations.core.composition)
    • Serialization API (albumentations.core.serialization)
    • Transforms Interface (albumentations.core.transforms_interface)
    • Helper functions for working with bounding boxes (albumentations.core.bbox_utils)
    • Helper functions for working with keypoints (albumentations.core.keypoints_utils)
  • Augmentations (albumentations.augmentations)
    • Transforms (albumentations.augmentations.transforms)
    • Functional transforms (albumentations.augmentations.functional)
  • PyTorch Helpers (albumentations.pytorch)
    • Transforms (albumentations.pytorch.transforms)
"},{"location":"api_reference/full_reference/","title":"Full API Reference on a single page","text":""},{"location":"api_reference/full_reference/#transform-types","title":"Transform Types","text":""},{"location":"api_reference/full_reference/#1-pixel-level-transforms","title":"1. Pixel-level transforms","text":"

Transforms that modify pixel values without changing spatial relationships. These can be safely applied to any target as they only affect the input image, leaving other targets (masks, bounding boxes, keypoints) unchanged.

  • AdditiveNoise
  • AdvancedBlur
  • AutoContrast
  • Blur
  • CLAHE
  • ChannelDropout
  • ChannelShuffle
  • ChromaticAberration
  • ColorJitter
  • Defocus
  • Downscale
  • Emboss
  • Equalize
  • FDA
  • FancyPCA
  • FromFloat
  • GaussNoise
  • GaussianBlur
  • GlassBlur
  • HistogramMatching
  • HueSaturationValue
  • ISONoise
  • Illumination
  • ImageCompression
  • InvertImg
  • MedianBlur
  • MotionBlur
  • MultiplicativeNoise
  • Normalize
  • PixelDistributionAdaptation
  • PlanckianJitter
  • PlasmaBrightnessContrast
  • PlasmaShadow
  • Posterize
  • RGBShift
  • RandomBrightnessContrast
  • RandomFog
  • RandomGamma
  • RandomGravel
  • RandomRain
  • RandomShadow
  • RandomSnow
  • RandomSunFlare
  • RandomToneCurve
  • RingingOvershoot
  • SaltAndPepper
  • Sharpen
  • ShotNoise
  • Solarize
  • Spatter
  • Superpixels
  • TemplateTransform
  • TextImage
  • ToFloat
  • ToGray
  • ToRGB
  • ToSepia
  • UnsharpMask
  • ZoomBlur
"},{"location":"api_reference/full_reference/#2-spatial-level-transforms","title":"2. Spatial-level transforms","text":"

Transforms that modify the spatial arrangement of pixels/features. Different targets have different spatial transform support - see the compatibility table below:

Transform Image Mask BBoxes Keypoints Volume Mask3D Affine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 AtLeastOneBBoxRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 BBoxSafeRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CenterCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CoarseDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Crop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropAndPad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropNonEmptyMaskIfExists \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 D4 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ElasticTransform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Erasing \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 FrequencyMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridElasticDeform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 HorizontalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Lambda \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 LongestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 MaskDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Morphological \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 NoOp \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OpticalDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OverlayElements \u2713 \u2713 Pad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PadIfNeeded \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Perspective \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PiecewiseAffine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PixelDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropFromBorders \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropNearBBox \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomGridShuffle \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomResizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomRotate90 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomScale \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedBBoxSafeCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Resize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Rotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SafeRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ShiftScaleRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SmallestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ThinPlateSpline \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeReverse \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Transpose \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 VerticalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 XYMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713"},{"location":"api_reference/full_reference/#3-volumetric-3d-transforms","title":"3. Volumetric (3D) transforms","text":"

Transforms designed for three-dimensional data (D, H, W). These operate on volumes and their corresponding 3D masks, supporting both single-channel and multi-channel data.

Transform Image Mask BBoxes Keypoints Volume Mask3D CenterCrop3D \u2713 \u2713 CoarseDropout3D \u2713 \u2713 CubicSymmetry \u2713 \u2713 Pad3D \u2713 \u2713 PadIfNeeded3D \u2713 \u2713 RandomCrop3D \u2713 \u2713"},{"location":"api_reference/augmentations/","title":"Index","text":"
  • Transforms (albumentations.augmentations.transforms)
  • Blur transforms (albumentations.augmentations.blur)
  • Crop transforms (albumentations.augmentations.crops)
  • Dropout transforms (albumentations.augmentations.dropout)
  • Geometric transforms (albumentations.augmentations.geometric)
  • Domain adaptation transforms (albumentations.augmentations.domain_adaptation)
  • Functional transforms (albumentations.augmentations.functional)
"},{"location":"api_reference/augmentations/domain_adaptation/","title":"Domain adaptation transforms (augmentations.domain_adaptation)","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional","title":"functional","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.apply_histogram","title":"def apply_histogram (img, reference_image, blend_ratio) [view source on GitHub]","text":"

Apply histogram matching to an input image using a reference image and blend the result.

This function performs histogram matching between the input image and a reference image, then blends the result with the original input image based on the specified blend ratio.

Parameters:

Name Type Description img np.ndarray

The input image to be transformed. Can be either grayscale or RGB. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

reference_image np.ndarray

The reference image used for histogram matching. Should have the same number of channels as the input image. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

blend_ratio float

The ratio for blending the matched image with the original image. Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.

Returns:

Type Description np.ndarray

The transformed image after histogram matching and blending. The output will have the same shape and dtype as the input image.

Supported image types: - Grayscale images: 2D arrays - RGB images: 3D arrays with 3 channels - Multispectral images: 3D arrays with more than 3 channels

Note

  • If the input and reference images have different sizes, the reference image will be resized to match the input image's dimensions.
  • The function uses a custom implementation of histogram matching based on OpenCV and NumPy.
  • The @clipped and @preserve_channel_dim decorators ensure the output is within the valid range and maintains the original number of dimensions.
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef apply_histogram(img: np.ndarray, reference_image: np.ndarray, blend_ratio: float) -> np.ndarray:\n    \"\"\"Apply histogram matching to an input image using a reference image and blend the result.\n\n    This function performs histogram matching between the input image and a reference image,\n    then blends the result with the original input image based on the specified blend ratio.\n\n    Args:\n        img (np.ndarray): The input image to be transformed. Can be either grayscale or RGB.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        reference_image (np.ndarray): The reference image used for histogram matching.\n            Should have the same number of channels as the input image.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        blend_ratio (float): The ratio for blending the matched image with the original image.\n            Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.\n\n    Returns:\n        np.ndarray: The transformed image after histogram matching and blending.\n            The output will have the same shape and dtype as the input image.\n\n    Supported image types:\n        - Grayscale images: 2D arrays\n        - RGB images: 3D arrays with 3 channels\n        - Multispectral images: 3D arrays with more than 3 channels\n\n    Note:\n        - If the input and reference images have different sizes, the reference image\n          will be resized to match the input image's dimensions.\n        - The function uses a custom implementation of histogram matching based on OpenCV and NumPy.\n        - The @clipped and @preserve_channel_dim decorators ensure the output is within\n          the valid range and maintains the original number of dimensions.\n    \"\"\"\n    # Resize reference image only if necessary\n    if img.shape[:2] != reference_image.shape[:2]:\n        reference_image = cv2.resize(reference_image, dsize=(img.shape[1], img.shape[0]))\n\n    img = np.squeeze(img)\n    reference_image = np.squeeze(reference_image)\n\n    # Match histograms between the images\n    matched = match_histograms(img, reference_image)\n\n    # Blend the original image and the matched image\n    return add_weighted(matched, blend_ratio, img, 1 - blend_ratio)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.fourier_domain_adaptation","title":"def fourier_domain_adaptation (img, target_img, beta) [view source on GitHub]","text":"

Apply Fourier Domain Adaptation to the input image using a target image.

This function performs domain adaptation in the frequency domain by modifying the amplitude spectrum of the source image based on the target image's amplitude spectrum. It preserves the phase information of the source image, which helps maintain its content while adapting its style to match the target image.

Parameters:

Name Type Description img np.ndarray

The source image to be adapted. Can be grayscale or RGB.

target_img np.ndarray

The target image used as a reference for adaptation. Should have the same dimensions as the source image.

beta float

The adaptation strength, typically in the range [0, 1]. Higher values result in stronger adaptation towards the target image's style.

Returns:

Type Description np.ndarray

The adapted image with the same shape and type as the input image.

Exceptions:

Type Description ValueError

If the source and target images have different shapes.

Note

  • Both input images are converted to float32 for processing.
  • The function handles both grayscale (2D) and color (3D) images.
  • For grayscale images, an extra dimension is added to facilitate uniform processing.
  • The adaptation is performed channel-wise for color images.
  • The output is clipped to the valid range and preserves the original number of channels.

The adaptation process involves the following steps for each channel: 1. Compute the 2D Fourier Transform of both source and target images. 2. Shift the zero frequency component to the center of the spectrum. 3. Extract amplitude and phase information from the source image's spectrum. 4. Mutate the source amplitude using the target amplitude and the beta parameter. 5. Combine the mutated amplitude with the original phase. 6. Perform the inverse Fourier Transform to obtain the adapted channel.

The low_freq_mutate function (not shown here) is responsible for the actual amplitude mutation, focusing on low-frequency components which carry style information.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n>>> assert adapted_img.shape == source_img.shape\n

References

  • \"FDA: Fourier Domain Adaptation for Semantic Segmentation\" (Yang and Soatto, 2020, CVPR) https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef fourier_domain_adaptation(img: np.ndarray, target_img: np.ndarray, beta: float) -> np.ndarray:\n    \"\"\"Apply Fourier Domain Adaptation to the input image using a target image.\n\n    This function performs domain adaptation in the frequency domain by modifying the amplitude\n    spectrum of the source image based on the target image's amplitude spectrum. It preserves\n    the phase information of the source image, which helps maintain its content while adapting\n    its style to match the target image.\n\n    Args:\n        img (np.ndarray): The source image to be adapted. Can be grayscale or RGB.\n        target_img (np.ndarray): The target image used as a reference for adaptation.\n            Should have the same dimensions as the source image.\n        beta (float): The adaptation strength, typically in the range [0, 1].\n            Higher values result in stronger adaptation towards the target image's style.\n\n    Returns:\n        np.ndarray: The adapted image with the same shape and type as the input image.\n\n    Raises:\n        ValueError: If the source and target images have different shapes.\n\n    Note:\n        - Both input images are converted to float32 for processing.\n        - The function handles both grayscale (2D) and color (3D) images.\n        - For grayscale images, an extra dimension is added to facilitate uniform processing.\n        - The adaptation is performed channel-wise for color images.\n        - The output is clipped to the valid range and preserves the original number of channels.\n\n    The adaptation process involves the following steps for each channel:\n    1. Compute the 2D Fourier Transform of both source and target images.\n    2. Shift the zero frequency component to the center of the spectrum.\n    3. Extract amplitude and phase information from the source image's spectrum.\n    4. Mutate the source amplitude using the target amplitude and the beta parameter.\n    5. Combine the mutated amplitude with the original phase.\n    6. Perform the inverse Fourier Transform to obtain the adapted channel.\n\n    The `low_freq_mutate` function (not shown here) is responsible for the actual\n    amplitude mutation, focusing on low-frequency components which carry style information.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n        >>> assert adapted_img.shape == source_img.shape\n\n    References:\n        - \"FDA: Fourier Domain Adaptation for Semantic Segmentation\"\n          (Yang and Soatto, 2020, CVPR)\n          https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n    \"\"\"\n    src_img = img.astype(np.float32)\n    trg_img = target_img.astype(np.float32)\n\n    if src_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        src_img = np.expand_dims(src_img, axis=-1)\n    if trg_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        trg_img = np.expand_dims(trg_img, axis=-1)\n\n    num_channels = src_img.shape[-1]\n\n    # Prepare container for the output image\n    src_in_trg = np.zeros_like(src_img)\n\n    for channel_id in range(num_channels):\n        # Perform FFT on each channel\n        fft_src = np.fft.fft2(src_img[:, :, channel_id])\n        fft_trg = np.fft.fft2(trg_img[:, :, channel_id])\n\n        # Shift the zero frequency component to the center\n        fft_src_shifted = np.fft.fftshift(fft_src)\n        fft_trg_shifted = np.fft.fftshift(fft_trg)\n\n        # Extract amplitude and phase\n        amp_src, pha_src = np.abs(fft_src_shifted), np.angle(fft_src_shifted)\n        amp_trg = np.abs(fft_trg_shifted)\n\n        # Mutate the amplitude part of the source with the target\n        mutated_amp = low_freq_mutate(amp_src.copy(), amp_trg, beta)\n\n        # Combine the mutated amplitude with the original phase\n        fft_src_mutated = np.fft.ifftshift(mutated_amp * np.exp(1j * pha_src))\n\n        # Perform inverse FFT\n        src_in_trg_channel = np.fft.ifft2(fft_src_mutated)\n\n        # Store the result in the corresponding channel of the output image\n        src_in_trg[:, :, channel_id] = np.real(src_in_trg_channel)\n\n    return src_in_trg\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.match_histograms","title":"def match_histograms (image, reference) [view source on GitHub]","text":"

Adjust an image so that its cumulative histogram matches that of another.

The adjustment is applied separately for each channel.

Parameters:

Name Type Description image np.ndarray

Input image. Can be gray-scale or in color.

reference np.ndarray

Image to match histogram of. Must have the same number of channels as image.

channel_axis

If None, the image is assumed to be a grayscale (single channel) image. Otherwise, this parameter indicates which axis of the array corresponds to channels.

Returns:

Type Description np.ndarray

Transformed input image.

Exceptions:

Type Description ValueError

Thrown when the number of channels in the input image and the reference differ.

Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef match_histograms(image: np.ndarray, reference: np.ndarray) -> np.ndarray:\n    \"\"\"Adjust an image so that its cumulative histogram matches that of another.\n\n    The adjustment is applied separately for each channel.\n\n    Args:\n        image: Input image. Can be gray-scale or in color.\n        reference: Image to match histogram of. Must have the same number of channels as image.\n        channel_axis: If None, the image is assumed to be a grayscale (single channel) image.\n            Otherwise, this parameter indicates which axis of the array corresponds to channels.\n\n    Returns:\n        np.ndarray: Transformed input image.\n\n    Raises:\n        ValueError: Thrown when the number of channels in the input image and the reference differ.\n    \"\"\"\n    if reference.dtype != np.uint8:\n        reference = from_float(reference, np.uint8)\n\n    if image.ndim != reference.ndim:\n        raise ValueError(\"Image and reference must have the same number of dimensions.\")\n\n    # Expand dimensions for grayscale images\n    if image.ndim == 2:\n        image = np.expand_dims(image, axis=-1)\n    if reference.ndim == 2:\n        reference = np.expand_dims(reference, axis=-1)\n\n    matched = np.empty(image.shape, dtype=np.uint8)\n\n    num_channels = image.shape[-1]\n\n    for channel in range(num_channels):\n        matched_channel = _match_cumulative_cdf(image[..., channel], reference[..., channel]).astype(np.uint8)\n        matched[..., channel] = matched_channel\n\n    return matched\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms","title":"transforms","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.FDA","title":"class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Reference

  • https://github.com/YanchaoYang/FDA
  • https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n>>> result = aug(image=image)\n

Note

FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target domain samples are unavailable. It enables significant improvements in model generalization by aligning the low-level statistics of source and target images through a simple yet effective Fourier-based method.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class FDA(ImageOnlyTransform):\n    \"\"\"Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation\n    (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source\n    and target datasets, effectively adapting images from one domain to closely resemble those from another without\n    altering their semantic content.\n\n    This transform is particularly beneficial in scenarios where the training (source) and testing (target) images\n    come from different distributions, such as synthetic versus real images, or day versus night scenes.\n    Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain\n    alignment by swapping low-frequency components of the Fourier transform between the source and target images.\n    This technique has shown to improve the performance of models on the target domain, particularly for tasks\n    like semantic segmentation, without additional training for domain invariance.\n\n    The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more\n    of the original image's characteristics and higher values leading to more pronounced adaptation effects.\n    It is recommended to use beta values less than 0.3 to avoid introducing artifacts.\n\n    Args:\n        reference_images (Sequence[Any]): Sequence of objects to be converted into images by `read_fn`. This typically\n            involves paths to images that serve as target domain examples for adaptation.\n        beta_limit (tuple[float, float] | float): Coefficient beta from the paper, controlling the swapping extent of\n            frequency components. If one value is provided beta will be sampled from uniform\n            distribution [0, beta_limit]. Values should be less than 0.5.\n        read_fn (Callable): User-defined function for reading images. It takes an element from `reference_images` and\n            returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a\n            numpy array.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        - https://github.com/YanchaoYang/FDA\n        - https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n        >>> result = aug(image=image)\n\n    Note:\n        FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target\n        domain samples are unavailable. It enables significant improvements in model generalization by aligning\n        the low-level statistics of source and target images through a simple yet effective Fourier-based method.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        read_fn: Callable[[Any], np.ndarray]\n        beta_limit: ZeroOneRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_ranges(cls, value: tuple[float, float]) -> tuple[float, float]:\n            bounds = 0, MAX_BETA_LIMIT\n            if not bounds[0] <= value[0] <= value[1] <= bounds[1]:\n                raise ValueError(f\"Values should be in the range {bounds} got {value} \")\n            return value\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        beta_limit: ScaleFloatType = (0, 0.1),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        target_image: np.ndarray,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fourier_domain_adaptation(img, target_image, beta)\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n        target_img = self.read_fn(self.py_random.choice(self.reference_images))\n        target_img = cv2.resize(target_img, dsize=(width, height))\n\n        return {\"target_image\": target_img, \"beta\": self.py_random.uniform(*self.beta_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"reference_images\", \"beta_limit\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"FDA can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.HistogramMatching","title":"class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.HistogramMatching(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     read_fn=lambda x: x,\n...     p=1\n... )\n>>> result = transform(image=image)\n>>> matched_image = result[\"image\"]\n

References

  • Histogram Matching in scikit-image: https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class HistogramMatching(ImageOnlyTransform):\n    \"\"\"Adjust the pixel values of an input image to match the histogram of a reference image.\n\n    This transform applies histogram matching, a technique that modifies the distribution of pixel\n    intensities in the input image to closely resemble that of a reference image. This process is\n    performed independently for each channel in multi-channel images, provided both the input and\n    reference images have the same number of channels.\n\n    Histogram matching is particularly useful for:\n    - Normalizing images from different sources or captured under varying conditions.\n    - Preparing images for feature matching or other computer vision tasks where consistent\n      tone and contrast are important.\n    - Simulating different lighting or camera conditions in a controlled manner.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of reference image sources. These can be\n            file paths, URLs, or any objects that can be converted to images by the `read_fn`.\n        blend_ratio (tuple[float, float]): Range for the blending factor between the original\n            and the matched image. Must be two floats between 0 and 1, where:\n            - 0 means no blending (original image is returned)\n            - 1 means full histogram matching\n            A random value within this range is chosen for each application.\n            Default: (0.5, 1.0)\n        read_fn (Callable[[Any], np.ndarray]): A function that takes an element from\n            `reference_images` and returns a numpy array representing the image.\n            Default: read_rgb_image (reads image file from disk)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform cannot be directly serialized due to its dependency on external image data.\n        - The effectiveness of the matching depends on the similarity between the input and reference images.\n        - For best results, choose reference images that represent the desired tone and contrast.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.HistogramMatching(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     read_fn=lambda x: x,\n        ...     p=1\n        ... )\n        >>> result = transform(image=image)\n        >>> matched_image = result[\"image\"]\n\n    References:\n        - Histogram Matching in scikit-image:\n          https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.5, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n\n    def apply(\n        self: np.ndarray,\n        img: np.ndarray,\n        reference_image: np.ndarray,\n        blend_ratio: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return apply_histogram(img, reference_image, blend_ratio)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"HistogramMatching can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.PixelDistributionAdaptation","title":"class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub]","text":"

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal[\"pca\", \"standard\", \"minmax\"]

Specifies the type of statistical transformation to apply. - \"pca\": Principal Component Analysis - \"standard\": StandardScaler (zero mean and unit variance) - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: \"pca\"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PixelDistributionAdaptation(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     transform_type=\"standard\",\n...     read_fn=lambda x: x,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> adapted_image = result[\"image\"]\n

References

  • https://github.com/arsenyinfo/qudida
  • https://arxiv.org/abs/1911.11483

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class PixelDistributionAdaptation(ImageOnlyTransform):\n    \"\"\"Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image\n    with that of a reference image. This process involves fitting a simple statistical transformation\n    (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images,\n    transforming the original image with the transformation trained on it, and then applying the inverse\n    transformation using the transform fitted on the reference image. The result is an adapted image\n    that retains the original content while mimicking the pixel value distribution of the reference domain.\n\n    The process can be visualized as two main steps:\n    1. Adjusting the original image to a standard distribution space using a selected transform.\n    2. Moving the adjusted image into the distribution space of the reference image by applying the inverse\n       of the transform fitted on the reference image.\n\n    This technique is especially useful in scenarios where images from different domains (e.g., synthetic\n    vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in\n    image processing tasks.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of objects (typically image paths) that will be\n            converted into images by `read_fn`. These images serve as references for the domain adaptation.\n        blend_ratio (tuple[float, float]): Specifies the minimum and maximum blend ratio for mixing\n            the adapted image with the original. This enhances the diversity of the output images.\n            Values should be in the range [0, 1]. Default: (0.25, 1.0)\n        read_fn (Callable): A user-defined function for reading and converting the objects in\n            `reference_images` into numpy arrays. By default, it assumes these objects are image paths.\n        transform_type (Literal[\"pca\", \"standard\", \"minmax\"]): Specifies the type of statistical\n            transformation to apply.\n            - \"pca\": Principal Component Analysis\n            - \"standard\": StandardScaler (zero mean and unit variance)\n            - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1])\n            Default: \"pca\"\n        p (float): The probability of applying the transform to any given image. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effectiveness of the adaptation depends on the similarity between the input and reference domains.\n        - PCA transformation may alter color relationships more significantly than other methods.\n        - StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.\n        - The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.\n        - This transform cannot be directly serialized due to its dependency on external image data.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PixelDistributionAdaptation(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     transform_type=\"standard\",\n        ...     read_fn=lambda x: x,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> adapted_image = result[\"image\"]\n\n    References:\n        - https://github.com/arsenyinfo/qudida\n        - https://arxiv.org/abs/1911.11483\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.25, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"] = \"pca\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n        self.transform_type = transform_type\n\n    def apply(self, img: np.ndarray, reference_image: np.ndarray, blend_ratio: float, **params: Any) -> np.ndarray:\n        return adapt_pixel_distribution(\n            img,\n            ref=reference_image,\n            weight=blend_ratio,\n            transform_type=self.transform_type,\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\", \"transform_type\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"PixelDistributionAdaptation can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.TemplateTransform","title":"class TemplateTransform (templates, img_weight=(0.5, 0.5), template_weight=None, template_transform=None, name=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply blending of input image with specified templates.

This transform overlays one or more template images onto the input image using alpha blending. It allows for creating complex composite images or simulating various visual effects.

Parameters:

Name Type Description templates numpy array | list[np.ndarray]

Images to use as templates for the transform. If a single numpy array is provided, it will be used as the only template. If a list of numpy arrays is provided, one will be randomly chosen for each application.

img_weight tuple[float, float] | float

Weight of the original image in the blend. If a single float, that value will always be used. If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application. To use a fixed weight, use (weight, weight). Default: (0.5, 0.5).

template_transform A.Compose | None

A composition of Albumentations transforms to apply to the template before blending. This should be an instance of A.Compose containing one or more Albumentations transforms. Default: None.

name str | None

Name of the transform instance. Used for serialization purposes. Default: None.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The template(s) must have the same number of channels as the input image or be single-channel.
  • If a single-channel template is used with a multi-channel image, the template will be replicated across all channels.
  • The template(s) will be resized to match the input image size if they differ.
  • To make this transform serializable, provide a name when initializing it.

Mathematical Formulation: Given: - I: Input image - T: Template image - w_i: Weight of input image (sampled from img_weight)

The blended image B is computed as:\n\nB = w_i * I + (1 - w_i) * T\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-a-single-template","title":"Apply template transform with a single template","text":"Python
>>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-multiple-templates-and-custom-weights","title":"Apply template transform with multiple templates and custom weights","text":"Python
>>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n>>> transform = A.TemplateTransform(\n...     templates=templates,\n...     img_weight=(0.3, 0.7),\n...     name=\"multi_template_transform\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-additional-transforms-on-the-template","title":"Apply template transform with additional transforms on the template","text":"Python
>>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n>>> transform = A.TemplateTransform(\n...     templates=template,\n...     img_weight=0.6,\n...     template_transform=template_transform,\n...     name=\"transformed_template\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Image blending: https://en.wikipedia.org/wiki/Image_blending

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class TemplateTransform(ImageOnlyTransform):\n    \"\"\"Apply blending of input image with specified templates.\n\n    This transform overlays one or more template images onto the input image using alpha blending.\n    It allows for creating complex composite images or simulating various visual effects.\n\n    Args:\n        templates (numpy array | list[np.ndarray]): Images to use as templates for the transform.\n            If a single numpy array is provided, it will be used as the only template.\n            If a list of numpy arrays is provided, one will be randomly chosen for each application.\n\n        img_weight (tuple[float, float]  | float): Weight of the original image in the blend.\n            If a single float, that value will always be used.\n            If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application.\n            To use a fixed weight, use (weight, weight).\n            Default: (0.5, 0.5).\n\n        template_transform (A.Compose | None): A composition of Albumentations transforms to apply to the template\n            before blending.\n            This should be an instance of A.Compose containing one or more Albumentations transforms.\n            Default: None.\n\n        name (str | None): Name of the transform instance. Used for serialization purposes.\n            Default: None.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The template(s) must have the same number of channels as the input image or be single-channel.\n        - If a single-channel template is used with a multi-channel image, the template will be replicated across\n          all channels.\n        - The template(s) will be resized to match the input image size if they differ.\n        - To make this transform serializable, provide a name when initializing it.\n\n    Mathematical Formulation:\n        Given:\n        - I: Input image\n        - T: Template image\n        - w_i: Weight of input image (sampled from img_weight)\n\n        The blended image B is computed as:\n\n        B = w_i * I + (1 - w_i) * T\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply template transform with a single template\n        >>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with multiple templates and custom weights\n        >>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n        >>> transform = A.TemplateTransform(\n        ...     templates=templates,\n        ...     img_weight=(0.3, 0.7),\n        ...     name=\"multi_template_transform\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with additional transforms on the template\n        >>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n        >>> transform = A.TemplateTransform(\n        ...     templates=template,\n        ...     img_weight=0.6,\n        ...     template_transform=template_transform,\n        ...     name=\"transformed_template\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Image blending: https://en.wikipedia.org/wiki/Image_blending\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        templates: np.ndarray | Sequence[np.ndarray]\n        img_weight: ZeroOneRangeType\n        template_weight: ZeroOneRangeType | None = Field(\n            deprecated=\"Template_weight is deprecated. Computed automatically as (1 - img_weight)\",\n        )\n        template_transform: Compose | BasicTransform | None = None\n        name: str | None\n\n        @field_validator(\"templates\")\n        @classmethod\n        def validate_templates(cls, v: np.ndarray | list[np.ndarray]) -> list[np.ndarray]:\n            if isinstance(v, np.ndarray):\n                return [v]\n            if isinstance(v, list):\n                if not all(isinstance(item, np.ndarray) for item in v):\n                    msg = \"All templates must be numpy arrays.\"\n                    raise ValueError(msg)\n                return v\n            msg = \"Templates must be a numpy array or a list of numpy arrays.\"\n            raise TypeError(msg)\n\n    def __init__(\n        self,\n        templates: np.ndarray | list[np.ndarray],\n        img_weight: ScaleFloatType = (0.5, 0.5),\n        template_weight: None = None,\n        template_transform: Compose | BasicTransform | None = None,\n        name: str | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.templates = templates\n        self.img_weight = cast(tuple[float, float], img_weight)\n        self.template_transform = template_transform\n        self.name = name\n\n    def apply(\n        self,\n        img: np.ndarray,\n        template: np.ndarray,\n        img_weight: float,\n        **params: Any,\n    ) -> np.ndarray:\n        if img_weight == 0:\n            return template\n        if img_weight == 1:\n            return img\n\n        return add_weighted(img, img_weight, template, 1 - img_weight)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"img_weight\": self.py_random.uniform(*self.img_weight),\n        }\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        template = self.py_random.choice(self.templates)\n\n        if self.template_transform is not None:\n            template = self.template_transform(image=template)[\"image\"]\n\n        if get_num_channels(template) not in [1, get_num_channels(image)]:\n            msg = (\n                \"Template must be a single channel or \"\n                \"has the same number of channels as input \"\n                f\"image ({get_num_channels(image)}), got {get_num_channels(template)}\"\n            )\n            raise ValueError(msg)\n\n        if template.dtype != image.dtype:\n            msg = \"Image and template must be the same image type\"\n            raise ValueError(msg)\n\n        if image.shape[:2] != template.shape[:2]:\n            template = fgeometric.resize(template, image.shape[:2], interpolation=cv2.INTER_AREA)\n\n        if get_num_channels(template) == 1 and get_num_channels(image) > 1:\n            # Replicate single channel template across all channels to match input image\n            template = cv2.merge([template] * get_num_channels(image))\n        # in order to support grayscale image with dummy dim\n        template = template.reshape(image.shape)\n\n        return {\"template\": template}\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a TemplateTransform serializable you should provide the `name` argument, \"\n                \"e.g. `TemplateTransform(name='my_transform', ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n
"},{"location":"api_reference/augmentations/functional/","title":"Functional transforms (augmentations.functional)","text":""},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_fog","title":"def add_fog (img, fog_intensity, alpha_coef, fog_particle_positions, fog_particle_radiuses) [view source on GitHub]","text":"

Add fog to the input image.

Parameters:

Name Type Description img np.ndarray

Input image.

fog_intensity float

Intensity of the fog effect, between 0 and 1.

alpha_coef float

Base alpha (transparency) value for fog particles.

fog_particle_positions list[tuple[int, int]]

List of (x, y) coordinates for fog particles.

fog_particle_radiuses list[int]

List of radiuses for each fog particle.

Returns:

Type Description np.ndarray

Image with added fog effect.

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\n@preserve_channel_dim\ndef add_fog(\n    img: np.ndarray,\n    fog_intensity: float,\n    alpha_coef: float,\n    fog_particle_positions: list[tuple[int, int]],\n    fog_particle_radiuses: list[int],\n) -> np.ndarray:\n    \"\"\"Add fog to the input image.\n\n    Args:\n        img (np.ndarray): Input image.\n        fog_intensity (float): Intensity of the fog effect, between 0 and 1.\n        alpha_coef (float): Base alpha (transparency) value for fog particles.\n        fog_particle_positions (list[tuple[int, int]]): List of (x, y) coordinates for fog particles.\n        fog_particle_radiuses (list[int]): List of radiuses for each fog particle.\n\n    Returns:\n        np.ndarray: Image with added fog effect.\n    \"\"\"\n    height, width = img.shape[:2]\n    num_channels = get_num_channels(img)\n\n    fog_layer = np.zeros((height, width, num_channels), dtype=np.uint8)\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    for (x, y), radius in zip(fog_particle_positions, fog_particle_radiuses):\n        color = max_value if num_channels == 1 else (max_value,) * num_channels\n        cv2.circle(\n            fog_layer,\n            center=(x, y),\n            radius=radius,\n            color=color,\n            thickness=-1,\n        )\n\n    # Apply gaussian blur to the fog layer\n    fog_layer = cv2.GaussianBlur(fog_layer, (25, 25), 0)\n\n    # Blend the fog layer with the original image\n    alpha = np.mean(fog_layer, axis=2, keepdims=True) / max_value * alpha_coef * fog_intensity\n\n    result = img * (1 - alpha) + fog_layer * alpha\n\n    return clip(result, np.uint8, inplace=True)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_rain","title":"def add_rain (img, slant, drop_length, drop_width, drop_color, blur_value, brightness_coefficient, rain_drops) [view source on GitHub]","text":"

Adds rain drops to the image.

Parameters:

Name Type Description img np.ndarray

Input image.

slant int

The angle of the rain drops.

drop_length int

The length of each rain drop.

drop_width int

The width of each rain drop.

drop_color tuple[int, int, int]

The color of the rain drops in RGB format.

blur_value int

The size of the kernel used to blur the image. Rainy views are blurry.

brightness_coefficient float

Coefficient to adjust the brightness of the image. Rainy days are usually shady.

rain_drops list[tuple[int, int]]

A list of tuples where each tuple represents the (x, y) coordinates of the starting point of a rain drop.

Returns:

Type Description np.ndarray

Image with rain effect added.

Reference

https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef add_rain(\n    img: np.ndarray,\n    slant: int,\n    drop_length: int,\n    drop_width: int,\n    drop_color: tuple[int, int, int],\n    blur_value: int,\n    brightness_coefficient: float,\n    rain_drops: list[tuple[int, int]],\n) -> np.ndarray:\n    \"\"\"Adds rain drops to the image.\n\n    Args:\n        img (np.ndarray): Input image.\n        slant (int): The angle of the rain drops.\n        drop_length (int): The length of each rain drop.\n        drop_width (int): The width of each rain drop.\n        drop_color (tuple[int, int, int]): The color of the rain drops in RGB format.\n        blur_value (int): The size of the kernel used to blur the image. Rainy views are blurry.\n        brightness_coefficient (float): Coefficient to adjust the brightness of the image. Rainy days are usually shady.\n        rain_drops (list[tuple[int, int]]): A list of tuples where each tuple represents the (x, y)\n            coordinates of the starting point of a rain drop.\n\n    Returns:\n        np.ndarray: Image with rain effect added.\n\n    Reference:\n        https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    img = img.copy()\n    for rain_drop_x0, rain_drop_y0 in rain_drops:\n        rain_drop_x1 = rain_drop_x0 + slant\n        rain_drop_y1 = rain_drop_y0 + drop_length\n\n        cv2.line(\n            img,\n            (rain_drop_x0, rain_drop_y0),\n            (rain_drop_x1, rain_drop_y1),\n            drop_color,\n            drop_width,\n        )\n\n    img = cv2.blur(img, (blur_value, blur_value))  # rainy view are blurry\n    image_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)\n    image_hsv[:, :, 2] *= brightness_coefficient\n\n    return cv2.cvtColor(image_hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_shadow","title":"def add_shadow (img, vertices_list, intensities) [view source on GitHub]","text":"

Add shadows to the image by reducing the intensity of the pixel values in specified regions.

Parameters:

Name Type Description img np.ndarray

Input image. Multichannel images are supported.

vertices_list list[np.ndarray]

List of vertices for shadow polygons.

intensities np.ndarray

Array of shadow intensities. Range is [0, 1].

Returns:

Type Description np.ndarray

Image with shadows added.

Reference

https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef add_shadow(\n    img: np.ndarray,\n    vertices_list: list[np.ndarray],\n    intensities: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Add shadows to the image by reducing the intensity of the pixel values in specified regions.\n\n    Args:\n        img (np.ndarray): Input image. Multichannel images are supported.\n        vertices_list (list[np.ndarray]): List of vertices for shadow polygons.\n        intensities (np.ndarray): Array of shadow intensities. Range is [0, 1].\n\n    Returns:\n        np.ndarray: Image with shadows added.\n\n    Reference:\n        https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    num_channels = get_num_channels(img)\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    img_shadowed = img.copy()\n\n    # Iterate over the vertices and intensity list\n    for vertices, shadow_intensity in zip(vertices_list, intensities):\n        # Create mask for the current shadow polygon\n        mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)\n        cv2.fillPoly(mask, [vertices], (max_value,))\n\n        # Duplicate the mask to have the same number of channels as the image\n        mask = np.repeat(mask, num_channels, axis=2)\n\n        # Apply shadow to the channels directly\n        # It could be tempting to convert to HLS and apply the shadow to the L channel, but it creates artifacts\n        shadowed_indices = mask[:, :, 0] == max_value\n        darkness = 1 - shadow_intensity\n        img_shadowed[shadowed_indices] = clip(\n            img_shadowed[shadowed_indices] * darkness,\n            np.uint8,\n            inplace=True,\n        )\n\n    return img_shadowed\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_snow_bleach","title":"def add_snow_bleach (img, snow_point, brightness_coeff) [view source on GitHub]","text":"

Adds a simple snow effect to the image by bleaching out pixels.

This function simulates a basic snow effect by increasing the brightness of pixels that are above a certain threshold (snow_point). It operates in the HLS color space to modify the lightness channel.

Parameters:

Name Type Description img np.ndarray

Input image. Can be either RGB uint8 or float32.

snow_point float

A float in the range [0, 1], scaled and adjusted to determine the threshold for pixel modification. Higher values result in less snow effect.

brightness_coeff float

Coefficient applied to increase the brightness of pixels below the snow_point threshold. Larger values lead to more pronounced snow effects. Should be greater than 1.0 for a visible effect.

Returns:

Type Description np.ndarray

Image with simulated snow effect. The output has the same dtype as the input.

Note

  • This function converts the image to the HLS color space to modify the lightness channel.
  • The snow effect is created by selectively increasing the brightness of pixels.
  • This method tends to create a 'bleached' look, which may not be as realistic as more advanced snow simulation techniques.
  • The function automatically handles both uint8 and float32 input images.

The snow effect is created through the following steps: 1. Convert the image from RGB to HLS color space. 2. Adjust the snow_point threshold. 3. Increase the lightness of pixels below the threshold. 4. Convert the image back to RGB.

Mathematical Formulation: Let L be the lightness channel in HLS space. For each pixel (i, j): If L[i, j] < snow_point: L[i, j] = L[i, j] * brightness_coeff

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> snowy_image = A.functional.add_snow_v1(image, snow_point=0.5, brightness_coeff=1.5)\n

References

  • HLS Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV
  • Original implementation: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library
Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef add_snow_bleach(\n    img: np.ndarray,\n    snow_point: float,\n    brightness_coeff: float,\n) -> np.ndarray:\n    \"\"\"Adds a simple snow effect to the image by bleaching out pixels.\n\n    This function simulates a basic snow effect by increasing the brightness of pixels\n    that are above a certain threshold (snow_point). It operates in the HLS color space\n    to modify the lightness channel.\n\n    Args:\n        img (np.ndarray): Input image. Can be either RGB uint8 or float32.\n        snow_point (float): A float in the range [0, 1], scaled and adjusted to determine\n            the threshold for pixel modification. Higher values result in less snow effect.\n        brightness_coeff (float): Coefficient applied to increase the brightness of pixels\n            below the snow_point threshold. Larger values lead to more pronounced snow effects.\n            Should be greater than 1.0 for a visible effect.\n\n    Returns:\n        np.ndarray: Image with simulated snow effect. The output has the same dtype as the input.\n\n    Note:\n        - This function converts the image to the HLS color space to modify the lightness channel.\n        - The snow effect is created by selectively increasing the brightness of pixels.\n        - This method tends to create a 'bleached' look, which may not be as realistic as more\n          advanced snow simulation techniques.\n        - The function automatically handles both uint8 and float32 input images.\n\n    The snow effect is created through the following steps:\n    1. Convert the image from RGB to HLS color space.\n    2. Adjust the snow_point threshold.\n    3. Increase the lightness of pixels below the threshold.\n    4. Convert the image back to RGB.\n\n    Mathematical Formulation:\n        Let L be the lightness channel in HLS space.\n        For each pixel (i, j):\n        If L[i, j] < snow_point:\n            L[i, j] = L[i, j] * brightness_coeff\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> snowy_image = A.functional.add_snow_v1(image, snow_point=0.5, brightness_coeff=1.5)\n\n    References:\n        - HLS Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV\n        - Original implementation: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    snow_point *= max_value / 2\n    snow_point += max_value / 3\n\n    image_hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)\n    image_hls = np.array(image_hls, dtype=np.float32)\n\n    image_hls[:, :, 1][image_hls[:, :, 1] < snow_point] *= brightness_coeff\n\n    image_hls[:, :, 1] = clip(image_hls[:, :, 1], np.uint8, inplace=True)\n\n    image_hls = np.array(image_hls, dtype=np.uint8)\n\n    return cv2.cvtColor(image_hls, cv2.COLOR_HLS2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_snow_texture","title":"def add_snow_texture (img, snow_point, brightness_coeff, snow_texture, sparkle_mask) [view source on GitHub]","text":"

Add a realistic snow effect to the input image.

This function simulates snowfall by applying multiple visual effects to the image, including brightness adjustment, snow texture overlay, depth simulation, and color tinting. The result is a more natural-looking snow effect compared to simple pixel bleaching methods.

Parameters:

Name Type Description img np.ndarray

Input image in RGB format.

snow_point float

Coefficient that controls the amount and intensity of snow. Should be in the range [0, 1], where 0 means no snow and 1 means maximum snow effect.

brightness_coeff float

Coefficient for brightness adjustment to simulate the reflective nature of snow. Should be in the range [0, 1], where higher values result in a brighter image.

snow_texture np.ndarray

Snow texture.

sparkle_mask np.ndarray

Sparkle mask.

Returns:

Type Description np.ndarray

Image with added snow effect. The output has the same dtype as the input.

Note

  • The function first converts the image to HSV color space for better control over brightness and color adjustments.
  • A snow texture is generated using Gaussian noise and then filtered for a more natural appearance.
  • A depth effect is simulated, with more snow at the top of the image and less at the bottom.
  • A slight blue tint is added to simulate the cool color of snow.
  • Random sparkle effects are added to simulate light reflecting off snow crystals.

The snow effect is created through the following steps: 1. Brightness adjustment in HSV space 2. Generation of a snow texture using Gaussian noise 3. Application of a depth effect to the snow texture 4. Blending of the snow texture with the original image 5. Addition of a cool blue tint 6. Addition of sparkle effects

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> snowy_image = A.functional.add_snow_v2(image, snow_coeff=0.5, brightness_coeff=0.2)\n

Note

This function works with both uint8 and float32 image types, automatically handling the conversion between them.

References

  • Perlin Noise: https://en.wikipedia.org/wiki/Perlin_noise
  • HSV Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV
Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef add_snow_texture(\n    img: np.ndarray,\n    snow_point: float,\n    brightness_coeff: float,\n    snow_texture: np.ndarray,\n    sparkle_mask: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Add a realistic snow effect to the input image.\n\n    This function simulates snowfall by applying multiple visual effects to the image,\n    including brightness adjustment, snow texture overlay, depth simulation, and color tinting.\n    The result is a more natural-looking snow effect compared to simple pixel bleaching methods.\n\n    Args:\n        img (np.ndarray): Input image in RGB format.\n        snow_point (float): Coefficient that controls the amount and intensity of snow.\n            Should be in the range [0, 1], where 0 means no snow and 1 means maximum snow effect.\n        brightness_coeff (float): Coefficient for brightness adjustment to simulate the\n            reflective nature of snow. Should be in the range [0, 1], where higher values\n            result in a brighter image.\n        snow_texture (np.ndarray): Snow texture.\n        sparkle_mask (np.ndarray): Sparkle mask.\n\n    Returns:\n        np.ndarray: Image with added snow effect. The output has the same dtype as the input.\n\n    Note:\n        - The function first converts the image to HSV color space for better control over\n          brightness and color adjustments.\n        - A snow texture is generated using Gaussian noise and then filtered for a more\n          natural appearance.\n        - A depth effect is simulated, with more snow at the top of the image and less at the bottom.\n        - A slight blue tint is added to simulate the cool color of snow.\n        - Random sparkle effects are added to simulate light reflecting off snow crystals.\n\n    The snow effect is created through the following steps:\n    1. Brightness adjustment in HSV space\n    2. Generation of a snow texture using Gaussian noise\n    3. Application of a depth effect to the snow texture\n    4. Blending of the snow texture with the original image\n    5. Addition of a cool blue tint\n    6. Addition of sparkle effects\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> snowy_image = A.functional.add_snow_v2(image, snow_coeff=0.5, brightness_coeff=0.2)\n\n    Note:\n        This function works with both uint8 and float32 image types, automatically\n        handling the conversion between them.\n\n    References:\n        - Perlin Noise: https://en.wikipedia.org/wiki/Perlin_noise\n        - HSV Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    # Convert to HSV for better color control\n    img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)\n\n    # Increase brightness\n    img_hsv[:, :, 2] = np.clip(\n        img_hsv[:, :, 2] * (1 + brightness_coeff * snow_point),\n        0,\n        max_value,\n    )\n\n    # Generate snow texture\n    snow_texture = cv2.GaussianBlur(snow_texture, (0, 0), sigmaX=1, sigmaY=1)\n\n    # Create depth effect for snow simulation\n    # More snow accumulates at the top of the image, gradually decreasing towards the bottom\n    # This simulates natural snow distribution on surfaces\n    # The effect is achieved using a linear gradient from 1 (full snow) to 0.2 (less snow)\n    rows = img.shape[0]\n    depth_effect = np.linspace(1, 0.2, rows)[:, np.newaxis]\n    snow_texture *= depth_effect\n\n    # Apply snow texture\n    snow_layer = (np.dstack([snow_texture] * 3) * max_value * snow_point).astype(\n        np.float32,\n    )\n\n    # Blend snow with original image\n    img_with_snow = cv2.add(img_hsv, snow_layer)\n\n    # Add a slight blue tint to simulate cool snow color\n    blue_tint = np.full_like(img_with_snow, (0.6, 0.75, 1))  # Slight blue in HSV\n\n    img_with_snow = cv2.addWeighted(\n        img_with_snow,\n        0.85,\n        blue_tint,\n        0.15 * snow_point,\n        0,\n    )\n\n    # Convert back to RGB\n    img_with_snow = cv2.cvtColor(img_with_snow.astype(np.uint8), cv2.COLOR_HSV2RGB)\n\n    # Add some sparkle effects for snow glitter\n    img_with_snow[sparkle_mask] = [max_value, max_value, max_value]\n\n    return img_with_snow\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_sun_flare_overlay","title":"def add_sun_flare_overlay (img, flare_center, src_radius, src_color, circles) [view source on GitHub]","text":"

Add a sun flare effect to an image using a simple overlay technique.

This function creates a basic sun flare effect by overlaying multiple semi-transparent circles of varying sizes and intensities on the input image. The effect simulates a simple lens flare caused by bright light sources.

Parameters:

Name Type Description img np.ndarray

The input image.

flare_center tuple[float, float]

(x, y) coordinates of the flare center in pixel coordinates.

src_radius int

The radius of the main sun circle in pixels.

src_color tuple[int, ...]

The color of the sun, represented as a tuple of RGB values.

circles list[Any]

A list of tuples, each representing a circle that contributes to the flare effect. Each tuple contains: - alpha (float): The transparency of the circle (0.0 to 1.0). - center (tuple[int, int]): (x, y) coordinates of the circle center. - radius (int): The radius of the circle. - color (tuple[int, int, int]): RGB color of the circle.

Returns:

Type Description np.ndarray

The output image with the sun flare effect added.

Note

  • This function uses a simple alpha blending technique to overlay flare elements.
  • The main sun is created as a gradient circle, fading from the center outwards.
  • Additional flare circles are added along an imaginary line from the sun's position.
  • This method is computationally efficient but may produce less realistic results compared to more advanced techniques.

The flare effect is created through the following steps: 1. Create an overlay image and output image as copies of the input. 2. Add smaller flare circles to the overlay. 3. Blend the overlay with the output image using alpha compositing. 4. Add the main sun circle with a radial gradient.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> flare_center = (50, 50)\n>>> src_radius = 20\n>>> src_color = (255, 255, 200)\n>>> circles = [\n...     (0.1, (60, 60), 5, (255, 200, 200)),\n...     (0.2, (70, 70), 3, (200, 255, 200))\n... ]\n>>> flared_image = A.functional.add_sun_flare_overlay(\n...     image, flare_center, src_radius, src_color, circles\n... )\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\n@maybe_process_in_chunks\ndef add_sun_flare_overlay(\n    img: np.ndarray,\n    flare_center: tuple[float, float],\n    src_radius: int,\n    src_color: tuple[int, ...],\n    circles: list[Any],\n) -> np.ndarray:\n    \"\"\"Add a sun flare effect to an image using a simple overlay technique.\n\n    This function creates a basic sun flare effect by overlaying multiple semi-transparent\n    circles of varying sizes and intensities on the input image. The effect simulates\n    a simple lens flare caused by bright light sources.\n\n    Args:\n        img (np.ndarray): The input image.\n        flare_center (tuple[float, float]): (x, y) coordinates of the flare center\n            in pixel coordinates.\n        src_radius (int): The radius of the main sun circle in pixels.\n        src_color (tuple[int, ...]): The color of the sun, represented as a tuple of RGB values.\n        circles (list[Any]): A list of tuples, each representing a circle that contributes\n            to the flare effect. Each tuple contains:\n            - alpha (float): The transparency of the circle (0.0 to 1.0).\n            - center (tuple[int, int]): (x, y) coordinates of the circle center.\n            - radius (int): The radius of the circle.\n            - color (tuple[int, int, int]): RGB color of the circle.\n\n    Returns:\n        np.ndarray: The output image with the sun flare effect added.\n\n    Note:\n        - This function uses a simple alpha blending technique to overlay flare elements.\n        - The main sun is created as a gradient circle, fading from the center outwards.\n        - Additional flare circles are added along an imaginary line from the sun's position.\n        - This method is computationally efficient but may produce less realistic results\n          compared to more advanced techniques.\n\n    The flare effect is created through the following steps:\n    1. Create an overlay image and output image as copies of the input.\n    2. Add smaller flare circles to the overlay.\n    3. Blend the overlay with the output image using alpha compositing.\n    4. Add the main sun circle with a radial gradient.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> flare_center = (50, 50)\n        >>> src_radius = 20\n        >>> src_color = (255, 255, 200)\n        >>> circles = [\n        ...     (0.1, (60, 60), 5, (255, 200, 200)),\n        ...     (0.2, (70, 70), 3, (200, 255, 200))\n        ... ]\n        >>> flared_image = A.functional.add_sun_flare_overlay(\n        ...     image, flare_center, src_radius, src_color, circles\n        ... )\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n    \"\"\"\n    overlay = img.copy()\n    output = img.copy()\n\n    weighted_brightness = 0.0\n    total_radius_length = 0.0\n\n    for alpha, (x, y), rad3, circle_color in circles:\n        weighted_brightness += alpha * rad3\n        total_radius_length += rad3\n        cv2.circle(overlay, (x, y), rad3, circle_color, -1)\n        output = add_weighted(overlay, alpha, output, 1 - alpha)\n\n    point = [int(x) for x in flare_center]\n\n    overlay = output.copy()\n    num_times = src_radius // 10\n\n    # max_alpha is calculated using weighted_brightness and total_radii_length times 5\n    # meaning the higher the alpha with larger area, the brighter the bright spot will be\n    # for list of alphas in range [0.05, 0.2], the max_alpha should below 1\n    max_alpha = weighted_brightness / total_radius_length * 5\n    alpha = np.linspace(0.0, min(max_alpha, 1.0), num=num_times)\n\n    rad = np.linspace(1, src_radius, num=num_times)\n\n    for i in range(num_times):\n        cv2.circle(overlay, point, int(rad[i]), src_color, -1)\n        alp = alpha[num_times - i - 1] * alpha[num_times - i - 1] * alpha[num_times - i - 1]\n        output = add_weighted(overlay, alp, output, 1 - alp)\n\n    return output\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_sun_flare_physics_based","title":"def add_sun_flare_physics_based (img, flare_center, src_radius, src_color, circles) [view source on GitHub]","text":"

Add a more realistic sun flare effect to the image.

This function creates a complex sun flare effect by simulating various optical phenomena that occur in real camera lenses when capturing bright light sources. The result is a more realistic and physically plausible lens flare effect.

Parameters:

Name Type Description img np.ndarray

Input image.

flare_center tuple[int, int]

(x, y) coordinates of the sun's center in pixels.

src_radius int

Radius of the main sun circle in pixels.

src_color tuple[int, int, int]

Color of the sun in RGB format.

circles list[Any]

List of tuples, each representing a flare circle with parameters: (alpha, center, size, color) - alpha (float): Transparency of the circle (0.0 to 1.0). - center (tuple[int, int]): (x, y) coordinates of the circle center. - size (float): Size factor for the circle radius. - color (tuple[int, int, int]): RGB color of the circle.

Returns:

Type Description np.ndarray

Image with added sun flare effect.

Note

This function implements several techniques to create a more realistic flare: 1. Separate flare layer: Allows for complex manipulations of the flare effect. 2. Lens diffraction spikes: Simulates light diffraction in camera aperture. 3. Radial gradient mask: Creates natural fading of the flare from the center. 4. Gaussian blur: Softens the flare for a more natural glow effect. 5. Chromatic aberration: Simulates color fringing often seen in real lens flares. 6. Screen blending: Provides a more realistic blending of the flare with the image.

The flare effect is created through the following steps: 1. Create a separate flare layer. 2. Add the main sun circle and diffraction spikes to the flare layer. 3. Add additional flare circles based on the input parameters. 4. Apply Gaussian blur to soften the flare. 5. Create and apply a radial gradient mask for natural fading. 6. Simulate chromatic aberration by applying different blurs to color channels. 7. Blend the flare with the original image using screen blending mode.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n>>> flare_center = (500, 500)\n>>> src_radius = 50\n>>> src_color = (255, 255, 200)\n>>> circles = [\n...     (0.1, (550, 550), 10, (255, 200, 200)),\n...     (0.2, (600, 600), 5, (200, 255, 200))\n... ]\n>>> flared_image = A.functional.add_sun_flare_physics_based(\n...     image, flare_center, src_radius, src_color, circles\n... )\n

References

  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
  • Diffraction: https://en.wikipedia.org/wiki/Diffraction
  • Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration
  • Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef add_sun_flare_physics_based(\n    img: np.ndarray,\n    flare_center: tuple[int, int],\n    src_radius: int,\n    src_color: tuple[int, int, int],\n    circles: list[Any],\n) -> np.ndarray:\n    \"\"\"Add a more realistic sun flare effect to the image.\n\n    This function creates a complex sun flare effect by simulating various optical phenomena\n    that occur in real camera lenses when capturing bright light sources. The result is a\n    more realistic and physically plausible lens flare effect.\n\n    Args:\n        img (np.ndarray): Input image.\n        flare_center (tuple[int, int]): (x, y) coordinates of the sun's center in pixels.\n        src_radius (int): Radius of the main sun circle in pixels.\n        src_color (tuple[int, int, int]): Color of the sun in RGB format.\n        circles (list[Any]): List of tuples, each representing a flare circle with parameters:\n            (alpha, center, size, color)\n            - alpha (float): Transparency of the circle (0.0 to 1.0).\n            - center (tuple[int, int]): (x, y) coordinates of the circle center.\n            - size (float): Size factor for the circle radius.\n            - color (tuple[int, int, int]): RGB color of the circle.\n\n    Returns:\n        np.ndarray: Image with added sun flare effect.\n\n    Note:\n        This function implements several techniques to create a more realistic flare:\n        1. Separate flare layer: Allows for complex manipulations of the flare effect.\n        2. Lens diffraction spikes: Simulates light diffraction in camera aperture.\n        3. Radial gradient mask: Creates natural fading of the flare from the center.\n        4. Gaussian blur: Softens the flare for a more natural glow effect.\n        5. Chromatic aberration: Simulates color fringing often seen in real lens flares.\n        6. Screen blending: Provides a more realistic blending of the flare with the image.\n\n    The flare effect is created through the following steps:\n    1. Create a separate flare layer.\n    2. Add the main sun circle and diffraction spikes to the flare layer.\n    3. Add additional flare circles based on the input parameters.\n    4. Apply Gaussian blur to soften the flare.\n    5. Create and apply a radial gradient mask for natural fading.\n    6. Simulate chromatic aberration by applying different blurs to color channels.\n    7. Blend the flare with the original image using screen blending mode.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n        >>> flare_center = (500, 500)\n        >>> src_radius = 50\n        >>> src_color = (255, 255, 200)\n        >>> circles = [\n        ...     (0.1, (550, 550), 10, (255, 200, 200)),\n        ...     (0.2, (600, 600), 5, (200, 255, 200))\n        ... ]\n        >>> flared_image = A.functional.add_sun_flare_physics_based(\n        ...     image, flare_center, src_radius, src_color, circles\n        ... )\n\n    References:\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n        - Diffraction: https://en.wikipedia.org/wiki/Diffraction\n        - Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration\n        - Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen\n    \"\"\"\n    output = img.copy()\n    height, width = img.shape[:2]\n\n    # Create a separate flare layer\n    flare_layer = np.zeros_like(img, dtype=np.float32)\n\n    # Add the main sun\n    cv2.circle(flare_layer, flare_center, src_radius, src_color, -1)\n\n    # Add lens diffraction spikes\n    for angle in [0, 45, 90, 135]:\n        end_point = (\n            int(flare_center[0] + np.cos(np.radians(angle)) * max(width, height)),\n            int(flare_center[1] + np.sin(np.radians(angle)) * max(width, height)),\n        )\n        cv2.line(flare_layer, flare_center, end_point, src_color, 2)\n\n    # Add flare circles\n    for _, center, size, color in circles:\n        cv2.circle(flare_layer, center, int(size**0.33), color, -1)\n\n    # Apply gaussian blur to soften the flare\n    flare_layer = cv2.GaussianBlur(flare_layer, (0, 0), sigmaX=15, sigmaY=15)\n\n    # Create a radial gradient mask\n    y, x = np.ogrid[:height, :width]\n    mask = np.sqrt((x - flare_center[0]) ** 2 + (y - flare_center[1]) ** 2)\n    mask = 1 - np.clip(mask / (max(width, height) * 0.7), 0, 1)\n    mask = np.dstack([mask] * 3)\n\n    # Apply the mask to the flare layer\n    flare_layer *= mask\n\n    # Add chromatic aberration\n    channels = list(cv2.split(flare_layer))\n    channels[0] = cv2.GaussianBlur(\n        channels[0],\n        (0, 0),\n        sigmaX=3,\n        sigmaY=3,\n    )  # Blue channel\n    channels[2] = cv2.GaussianBlur(\n        channels[2],\n        (0, 0),\n        sigmaX=5,\n        sigmaY=5,\n    )  # Red channel\n    flare_layer = cv2.merge(channels)\n\n    # Blend the flare with the original image using screen blending\n    return 255 - ((255 - output) * (255 - flare_layer) / 255)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_corner_illumination","title":"def apply_corner_illumination (img, intensity, corner) [view source on GitHub]","text":"

Apply corner-based illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_corner_illumination(\n    img: np.ndarray,\n    intensity: float,\n    corner: Literal[0, 1, 2, 3],\n) -> np.ndarray:\n    \"\"\"Apply corner-based illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create distance map coordinates\n    y, x = np.ogrid[:height, :width]\n\n    # Adjust coordinates based on corner\n    if corner == 1:  # top-right\n        x = width - 1 - x\n    elif corner == 2:  # bottom-right\n        x = width - 1 - x\n        y = height - 1 - y\n    elif corner == 3:  # bottom-left\n        y = height - 1 - y\n\n    # Calculate normalized distance\n    distance = np.sqrt(x * x + y * y) / np.sqrt(height * height + width * width)\n    pattern = 1 - distance  # Invert so corner is brightest\n\n    return apply_illumination_pattern(result, pattern, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_gaussian_illumination","title":"def apply_gaussian_illumination (img, intensity, center, sigma) [view source on GitHub]","text":"

Apply gaussian illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_gaussian_illumination(\n    img: np.ndarray,\n    intensity: float,\n    center: tuple[float, float],\n    sigma: float,\n) -> np.ndarray:\n    \"\"\"Apply gaussian illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create coordinate grid\n    y, x = np.ogrid[:height, :width]\n\n    # Calculate gaussian pattern\n    center_x = width * center[0]\n    center_y = height * center[1]\n    sigma_pixels = max(height, width) * sigma\n    gaussian = np.exp(\n        -((x - center_x) ** 2 + (y - center_y) ** 2) / (2 * sigma_pixels**2),\n    )\n\n    return apply_illumination_pattern(result, gaussian, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_illumination_pattern","title":"def apply_illumination_pattern (img, pattern, intensity) [view source on GitHub]","text":"

Apply illumination pattern to image.

Parameters:

Name Type Description img np.ndarray

Input image

pattern np.ndarray

Illumination pattern of shape (H, W)

intensity float

Effect strength (-0.2 to 0.2)

Returns:

Type Description np.ndarray

Image with applied illumination

Source code in albumentations/augmentations/functional.py Python
def apply_illumination_pattern(\n    img: np.ndarray,\n    pattern: np.ndarray,\n    intensity: float,\n) -> np.ndarray:\n    \"\"\"Apply illumination pattern to image.\n\n    Args:\n        img: Input image\n        pattern: Illumination pattern of shape (H, W)\n        intensity: Effect strength (-0.2 to 0.2)\n\n    Returns:\n        Image with applied illumination\n    \"\"\"\n    if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n        pattern = pattern[..., np.newaxis]\n    return img * (1 + intensity * pattern)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_linear_illumination","title":"def apply_linear_illumination (img, intensity, angle) [view source on GitHub]","text":"

Apply linear gradient illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_linear_illumination(\n    img: np.ndarray,\n    intensity: float,\n    angle: float,\n) -> np.ndarray:\n    \"\"\"Apply linear gradient illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create gradient coordinates\n    y, x = np.ogrid[:height, :width]\n\n    # Calculate gradient direction\n    angle_rad = np.deg2rad(angle)\n    dx, dy = np.cos(angle_rad), np.sin(angle_rad)\n\n    # Create normalized gradient\n    gradient = (x * dx + y * dy) / np.sqrt(height * height + width * width)\n    gradient = (gradient + 1) / 2  # Normalize to [0, 1]\n\n    return apply_illumination_pattern(result, gradient, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_plasma_brightness_contrast","title":"def apply_plasma_brightness_contrast (img, brightness_factor, contrast_factor, plasma_pattern) [view source on GitHub]","text":"

Apply plasma-based brightness and contrast adjustments.

The plasma pattern is used to create spatially-varying adjustments: 1. Brightness is modified by adding the pattern * brightness_factor 2. Contrast is modified by interpolating between mean and original using the pattern * contrast_factor

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_plasma_brightness_contrast(\n    img: np.ndarray,\n    brightness_factor: float,\n    contrast_factor: float,\n    plasma_pattern: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply plasma-based brightness and contrast adjustments.\n\n    The plasma pattern is used to create spatially-varying adjustments:\n    1. Brightness is modified by adding the pattern * brightness_factor\n    2. Contrast is modified by interpolating between mean and original\n       using the pattern * contrast_factor\n    \"\"\"\n    result = img.copy()\n\n    max_value = MAX_VALUES_BY_DTYPE[img.dtype]\n\n    # Expand plasma pattern to match image dimensions\n    plasma_pattern = plasma_pattern[..., np.newaxis] if img.ndim > MONO_CHANNEL_DIMENSIONS else plasma_pattern\n\n    # Apply brightness adjustment\n    if brightness_factor != 0:\n        brightness_adjustment = plasma_pattern * brightness_factor * max_value\n        result = np.clip(result + brightness_adjustment, 0, max_value)\n\n    # Apply contrast adjustment\n    if contrast_factor != 0:\n        mean = result.mean()\n        contrast_weights = plasma_pattern * contrast_factor + 1\n        result = np.clip(mean + (result - mean) * contrast_weights, 0, max_value)\n\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_plasma_shadow","title":"def apply_plasma_shadow (img, intensity, plasma_pattern) [view source on GitHub]","text":"

Apply plasma-based shadow effect by darkening.

Parameters:

Name Type Description img np.ndarray

Input image

intensity float

Shadow intensity in [0, 1]

plasma_pattern np.ndarray

Generated plasma pattern of shape (H, W)

Returns:

Type Description np.ndarray

Image with applied shadow effect

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_plasma_shadow(\n    img: np.ndarray,\n    intensity: float,\n    plasma_pattern: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply plasma-based shadow effect by darkening.\n\n    Args:\n        img: Input image\n        intensity: Shadow intensity in [0, 1]\n        plasma_pattern: Generated plasma pattern of shape (H, W)\n\n    Returns:\n        Image with applied shadow effect\n    \"\"\"\n    result = img.copy()\n\n    # Expand dimensions to match image\n    plasma_pattern = plasma_pattern[..., np.newaxis] if img.ndim > MONO_CHANNEL_DIMENSIONS else plasma_pattern\n\n    # Apply shadow by darkening (multiplying by values < 1)\n    shadow_mask = 1 - plasma_pattern * intensity\n\n    return result * shadow_mask\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_salt_and_pepper","title":"def apply_salt_and_pepper (img, salt_mask, pepper_mask) [view source on GitHub]","text":"

Apply salt and pepper noise to image using pre-computed masks.

Parameters:

Name Type Description img np.ndarray

Input image

salt_mask np.ndarray

Boolean mask for salt (white) noise

pepper_mask np.ndarray

Boolean mask for pepper (black) noise

Returns:

Type Description np.ndarray

Image with applied salt and pepper noise

Source code in albumentations/augmentations/functional.py Python
def apply_salt_and_pepper(\n    img: np.ndarray,\n    salt_mask: np.ndarray,\n    pepper_mask: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply salt and pepper noise to image using pre-computed masks.\n\n    Args:\n        img: Input image\n        salt_mask: Boolean mask for salt (white) noise\n        pepper_mask: Boolean mask for pepper (black) noise\n\n    Returns:\n        Image with applied salt and pepper noise\n    \"\"\"\n    result = img.copy()\n\n    result[salt_mask] = MAX_VALUES_BY_DTYPE[img.dtype]\n    result[pepper_mask] = 0\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.auto_contrast","title":"def auto_contrast (img) [view source on GitHub]","text":"

Apply auto contrast to the image.

Auto contrast enhances image contrast by stretching the intensity range to use the full range while preserving relative intensities.

Parameters:

Name Type Description img np.ndarray

Input image in uint8 or float32 format.

Returns:

Type Description np.ndarray

Contrast-enhanced image in the same dtype as input.

Note

The function: 1. Computes histogram for each channel 2. Creates cumulative distribution 3. Normalizes to full intensity range 4. Uses lookup table for scaling

Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef auto_contrast(img: np.ndarray) -> np.ndarray:\n    \"\"\"Apply auto contrast to the image.\n\n    Auto contrast enhances image contrast by stretching the intensity range\n    to use the full range while preserving relative intensities.\n\n    Args:\n        img: Input image in uint8 or float32 format.\n\n    Returns:\n        Contrast-enhanced image in the same dtype as input.\n\n    Note:\n        The function:\n        1. Computes histogram for each channel\n        2. Creates cumulative distribution\n        3. Normalizes to full intensity range\n        4. Uses lookup table for scaling\n    \"\"\"\n    result = img.copy()\n    num_channels = get_num_channels(img)\n    max_value = MAX_VALUES_BY_DTYPE[img.dtype]\n\n    for i in range(num_channels):\n        channel = img[..., i] if img.ndim > MONO_CHANNEL_DIMENSIONS else img\n\n        # Compute histogram\n        hist = np.histogram(channel.flatten(), bins=256, range=(0, max_value))[0]\n\n        # Calculate cumulative distribution\n        cdf = hist.cumsum()\n\n        # Find the minimum and maximum non-zero values in the CDF\n        if cdf[cdf > 0].size == 0:\n            continue  # Skip if the channel is constant or empty\n\n        cdf_min = cdf[cdf > 0].min()\n        cdf_max = cdf.max()\n\n        if cdf_min == cdf_max:\n            continue\n\n        # Normalize CDF\n        cdf = (cdf - cdf_min) * max_value / (cdf_max - cdf_min)\n\n        # Create lookup table\n        lut = np.clip(np.around(cdf), 0, max_value).astype(np.uint8)\n\n        # Apply lookup table\n        if img.ndim > MONO_CHANNEL_DIMENSIONS:\n            result[..., i] = sz_lut(channel, lut)\n        else:\n            result = sz_lut(channel, lut)\n\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.clahe","title":"def clahe (img, clip_limit, tile_grid_size) [view source on GitHub]","text":"

Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.

This function enhances the contrast of the input image using CLAHE. For color images, it converts the image to the LAB color space, applies CLAHE to the L channel, and then converts the image back to RGB.

Parameters:

Name Type Description img np.ndarray

Input image. Can be grayscale (2D array) or RGB (3D array).

clip_limit float

Threshold for contrast limiting. Higher values give more contrast.

tile_grid_size tuple[int, int]

Size of grid for histogram equalization. Width and height of the grid.

Returns:

Type Description np.ndarray

Image with CLAHE applied. The output has the same dtype as the input.

Note

  • If the input image is float32, it's temporarily converted to uint8 for processing and then converted back to float32.
  • For color images, CLAHE is applied only to the luminance channel in the LAB color space.

Exceptions:

Type Description ValueError

If the input image is not 2D or 3D.

Examples:

Python
>>> import numpy as np\n>>> img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> result = clahe(img, clip_limit=2.0, tile_grid_size=(8, 8))\n>>> assert result.shape == img.shape\n>>> assert result.dtype == img.dtype\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef clahe(\n    img: np.ndarray,\n    clip_limit: float,\n    tile_grid_size: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.\n\n    This function enhances the contrast of the input image using CLAHE. For color images,\n    it converts the image to the LAB color space, applies CLAHE to the L channel, and then\n    converts the image back to RGB.\n\n    Args:\n        img (np.ndarray): Input image. Can be grayscale (2D array) or RGB (3D array).\n        clip_limit (float): Threshold for contrast limiting. Higher values give more contrast.\n        tile_grid_size (tuple[int, int]): Size of grid for histogram equalization.\n            Width and height of the grid.\n\n    Returns:\n        np.ndarray: Image with CLAHE applied. The output has the same dtype as the input.\n\n    Note:\n        - If the input image is float32, it's temporarily converted to uint8 for processing\n          and then converted back to float32.\n        - For color images, CLAHE is applied only to the luminance channel in the LAB color space.\n\n    Raises:\n        ValueError: If the input image is not 2D or 3D.\n\n    Example:\n        >>> import numpy as np\n        >>> img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> result = clahe(img, clip_limit=2.0, tile_grid_size=(8, 8))\n        >>> assert result.shape == img.shape\n        >>> assert result.dtype == img.dtype\n    \"\"\"\n    img = img.copy()\n    clahe_mat = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)\n\n    if is_grayscale_image(img):\n        return clahe_mat.apply(img)\n\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n\n    img[:, :, 0] = clahe_mat.apply(img[:, :, 0])\n\n    return cv2.cvtColor(img, cv2.COLOR_LAB2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.diamond_step","title":"def diamond_step (pattern, y, x, half, grid_size, roughness, random_generator) [view source on GitHub]","text":"

Compute edge value during diamond step.

Source code in albumentations/augmentations/functional.py Python
def diamond_step(\n    pattern: np.ndarray,\n    y: int,\n    x: int,\n    half: int,\n    grid_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Compute edge value during diamond step.\"\"\"\n    points = []\n    if y >= half:\n        points.append(pattern[y - half, x])\n    if y + half <= grid_size:\n        points.append(pattern[y + half, x])\n    if x >= half:\n        points.append(pattern[y, x - half])\n    if x + half <= grid_size:\n        points.append(pattern[y, x + half])\n\n    return sum(points) / len(points) + random_offset(\n        half * 2,\n        grid_size,\n        roughness,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.equalize","title":"def equalize (img, mask=None, mode='cv', by_channels=True) [view source on GitHub]","text":"

Apply histogram equalization to the input image.

This function enhances the contrast of the input image by equalizing its histogram. It supports both grayscale and color images, and can operate on individual channels or on the luminance channel of the image.

Parameters:

Name Type Description img np.ndarray

Input image. Can be grayscale (2D array) or RGB (3D array).

mask np.ndarray | None

Optional mask to apply the equalization selectively. If provided, must have the same shape as the input image. Default: None.

mode ImageMode

The backend to use for equalization. Can be either \"cv\" for OpenCV or \"pil\" for Pillow-style equalization. Default: \"cv\".

by_channels bool

If True, applies equalization to each channel independently. If False, converts the image to YCrCb color space and equalizes only the luminance channel. Only applicable to color images. Default: True.

Returns:

Type Description np.ndarray

Equalized image. The output has the same dtype as the input.

Exceptions:

Type Description ValueError

If the input image or mask have invalid shapes or types.

Note

  • If the input image is not uint8, it will be temporarily converted to uint8 for processing and then converted back to its original dtype.
  • For color images, when by_channels=False, the image is converted to YCrCb color space, equalized on the Y channel, and then converted back to RGB.
  • The function preserves the original number of channels in the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> equalized = A.equalize(image, mode=\"cv\", by_channels=True)\n>>> assert equalized.shape == image.shape\n>>> assert equalized.dtype == image.dtype\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef equalize(\n    img: np.ndarray,\n    mask: np.ndarray | None = None,\n    mode: ImageMode = \"cv\",\n    by_channels: bool = True,\n) -> np.ndarray:\n    \"\"\"Apply histogram equalization to the input image.\n\n    This function enhances the contrast of the input image by equalizing its histogram.\n    It supports both grayscale and color images, and can operate on individual channels\n    or on the luminance channel of the image.\n\n    Args:\n        img (np.ndarray): Input image. Can be grayscale (2D array) or RGB (3D array).\n        mask (np.ndarray | None): Optional mask to apply the equalization selectively.\n            If provided, must have the same shape as the input image. Default: None.\n        mode (ImageMode): The backend to use for equalization. Can be either \"cv\" for\n            OpenCV or \"pil\" for Pillow-style equalization. Default: \"cv\".\n        by_channels (bool): If True, applies equalization to each channel independently.\n            If False, converts the image to YCrCb color space and equalizes only the\n            luminance channel. Only applicable to color images. Default: True.\n\n    Returns:\n        np.ndarray: Equalized image. The output has the same dtype as the input.\n\n    Raises:\n        ValueError: If the input image or mask have invalid shapes or types.\n\n    Note:\n        - If the input image is not uint8, it will be temporarily converted to uint8\n          for processing and then converted back to its original dtype.\n        - For color images, when by_channels=False, the image is converted to YCrCb\n          color space, equalized on the Y channel, and then converted back to RGB.\n        - The function preserves the original number of channels in the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> equalized = A.equalize(image, mode=\"cv\", by_channels=True)\n        >>> assert equalized.shape == image.shape\n        >>> assert equalized.dtype == image.dtype\n    \"\"\"\n    _check_preconditions(img, mask, by_channels)\n\n    function = _equalize_pil if mode == \"pil\" else _equalize_cv\n\n    if is_grayscale_image(img):\n        return function(img, _handle_mask(mask))\n\n    if not by_channels:\n        result_img = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)\n        result_img[..., 0] = function(result_img[..., 0], _handle_mask(mask))\n        return cv2.cvtColor(result_img, cv2.COLOR_YCrCb2RGB)\n\n    result_img = np.empty_like(img)\n    for i in range(NUM_RGB_CHANNELS):\n        _mask = _handle_mask(mask, i)\n        result_img[..., i] = function(img[..., i], _mask)\n\n    return result_img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.fancy_pca","title":"def fancy_pca (img, alpha_vector) [view source on GitHub]","text":"

Perform 'Fancy PCA' augmentation on an image with any number of channels.

Parameters:

Name Type Description img np.ndarray

Input image

alpha_vector np.ndarray

Vector of scale factors for each principal component. Should have the same length as the number of channels in the image.

Returns:

Type Description np.ndarray

Augmented image of the same shape, type, and range as the input.

Image types: uint8, float32

Number of channels: Any

Note

  • This function generalizes the Fancy PCA augmentation to work with any number of channels.
  • It preserves the original range of the image ([0, 255] for uint8, [0, 1] for float32).
  • For single-channel images, the augmentation is applied as a simple scaling of pixel intensity variation.
  • For multi-channel images, PCA is performed on the entire image, treating each pixel as a point in N-dimensional space (where N is the number of channels).
  • The augmentation preserves the correlation between channels while adding controlled noise.
  • Computation time may increase significantly for images with a large number of channels.

Reference

Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).

Source code in albumentations/augmentations/functional.py Python
@float32_io\n@clipped\n@preserve_channel_dim\ndef fancy_pca(img: np.ndarray, alpha_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Perform 'Fancy PCA' augmentation on an image with any number of channels.\n\n    Args:\n        img (np.ndarray): Input image\n        alpha_vector (np.ndarray): Vector of scale factors for each principal component.\n                                   Should have the same length as the number of channels in the image.\n\n    Returns:\n        np.ndarray: Augmented image of the same shape, type, and range as the input.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This function generalizes the Fancy PCA augmentation to work with any number of channels.\n        - It preserves the original range of the image ([0, 255] for uint8, [0, 1] for float32).\n        - For single-channel images, the augmentation is applied as a simple scaling of pixel intensity variation.\n        - For multi-channel images, PCA is performed on the entire image, treating each pixel\n          as a point in N-dimensional space (where N is the number of channels).\n        - The augmentation preserves the correlation between channels while adding controlled noise.\n        - Computation time may increase significantly for images with a large number of channels.\n\n    Reference:\n        Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012).\n        ImageNet classification with deep convolutional neural networks.\n        In Advances in neural information processing systems (pp. 1097-1105).\n    \"\"\"\n    orig_shape = img.shape\n    num_channels = get_num_channels(img)\n\n    # Reshape image to 2D array of pixels\n    img_reshaped = img.reshape(-1, num_channels)\n\n    # Center the pixel values\n    img_mean = np.mean(img_reshaped, axis=0)\n    img_centered = img_reshaped - img_mean\n\n    if num_channels == 1:\n        # For grayscale images, apply a simple scaling\n        std_dev = np.std(img_centered)\n        noise = alpha_vector[0] * std_dev * img_centered\n    else:\n        # Compute covariance matrix\n        img_cov = np.cov(img_centered, rowvar=False)\n\n        # Compute eigenvectors & eigenvalues of the covariance matrix\n        eig_vals, eig_vecs = np.linalg.eigh(img_cov)\n\n        # Sort eigenvectors by eigenvalues in descending order\n        sort_perm = eig_vals[::-1].argsort()\n        eig_vals = eig_vals[sort_perm]\n        eig_vecs = eig_vecs[:, sort_perm]\n\n        # Create noise vector\n        noise = np.dot(\n            np.dot(eig_vecs, np.diag(alpha_vector * eig_vals)),\n            img_centered.T,\n        ).T\n\n    # Add noise to the image\n    img_pca = img_reshaped + noise\n\n    # Reshape back to original shape\n    img_pca = img_pca.reshape(orig_shape)\n\n    # Clip values to [0, 1] range\n    return np.clip(img_pca, 0, 1, out=img_pca)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_constant_noise","title":"def generate_constant_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate one value per channel.

Source code in albumentations/augmentations/functional.py Python
def generate_constant_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate one value per channel.\"\"\"\n    num_channels = shape[-1] if len(shape) > MONO_CHANNEL_DIMENSIONS else 1\n    return sample_noise(\n        noise_type,\n        (num_channels,),\n        params,\n        max_value,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_per_pixel_noise","title":"def generate_per_pixel_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate separate noise map for each channel.

Source code in albumentations/augmentations/functional.py Python
def generate_per_pixel_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate separate noise map for each channel.\"\"\"\n    return sample_noise(noise_type, shape, params, max_value, random_generator)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_plasma_pattern","title":"def generate_plasma_pattern (target_shape, size, roughness, random_generator) [view source on GitHub]","text":"

Generate a plasma fractal pattern using the Diamond-Square algorithm.

The Diamond-Square algorithm creates a natural-looking noise pattern by recursively subdividing a grid and adding random displacements at each step. The roughness parameter controls how quickly the random displacements decrease with each iteration.

Parameters:

Name Type Description target_shape tuple[int, int]

Final shape (height, width) of the pattern

size int

Initial size of the pattern grid. Will be rounded up to nearest power of 2. Larger values create more detailed patterns.

roughness float

Controls pattern roughness. Higher values create more rough/sharp transitions. Typical values are between 1.0 and 5.0.

random_generator np.random.Generator

NumPy random generator.

Returns:

Type Description np.ndarray

Normalized plasma pattern array of shape target_shape with values in [0, 1]

Source code in albumentations/augmentations/functional.py Python
def generate_plasma_pattern(\n    target_shape: tuple[int, int],\n    size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a plasma fractal pattern using the Diamond-Square algorithm.\n\n    The Diamond-Square algorithm creates a natural-looking noise pattern by recursively\n    subdividing a grid and adding random displacements at each step. The roughness\n    parameter controls how quickly the random displacements decrease with each iteration.\n\n    Args:\n        target_shape: Final shape (height, width) of the pattern\n        size: Initial size of the pattern grid. Will be rounded up to nearest power of 2.\n            Larger values create more detailed patterns.\n        roughness: Controls pattern roughness. Higher values create more rough/sharp transitions.\n            Typical values are between 1.0 and 5.0.\n        random_generator: NumPy random generator.\n\n    Returns:\n        Normalized plasma pattern array of shape target_shape with values in [0, 1]\n    \"\"\"\n    # Initialize grid\n    grid_size = get_grid_size(size, target_shape)\n    pattern = initialize_grid(grid_size, random_generator)\n\n    # Diamond-Square algorithm\n    step_size = grid_size\n    while step_size > 1:\n        half_step = step_size // 2\n\n        # Square step\n        for y in range(0, grid_size, step_size):\n            for x in range(0, grid_size, step_size):\n                if half_step > 0:\n                    pattern[y + half_step, x + half_step] = square_step(\n                        pattern,\n                        y,\n                        x,\n                        step_size,\n                        half_step,\n                        roughness,\n                        random_generator,\n                    )\n\n        # Diamond step\n        for y in range(0, grid_size + 1, half_step):\n            for x in range((y + half_step) % step_size, grid_size + 1, step_size):\n                pattern[y, x] = diamond_step(\n                    pattern,\n                    y,\n                    x,\n                    half_step,\n                    grid_size,\n                    roughness,\n                    random_generator,\n                )\n\n        step_size = half_step\n\n    min_pattern = pattern.min()\n\n    # Normalize to [0, 1] range\n    pattern = (pattern - min_pattern) / (pattern.max() - min_pattern)\n\n    return (\n        fgeometric.resize(pattern, target_shape, interpolation=cv2.INTER_LINEAR)\n        if pattern.shape != target_shape\n        else pattern\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_shared_noise","title":"def generate_shared_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate one noise map and broadcast to all channels.

Parameters:

Name Type Description noise_type Literal['uniform', 'gaussian', 'laplace', 'beta']

Type of noise distribution to use

shape tuple[int, ...]

Shape of the input image (H, W) or (H, W, C)

params dict[str, Any]

Parameters for the noise distribution

max_value float

Maximum value for the noise distribution

random_generator np.random.Generator

NumPy random generator instance

Returns:

Type Description np.ndarray

Noise array of shape (H, W) or (H, W, C) where the same noise pattern is shared across all channels

Source code in albumentations/augmentations/functional.py Python
def generate_shared_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate one noise map and broadcast to all channels.\n\n    Args:\n        noise_type: Type of noise distribution to use\n        shape: Shape of the input image (H, W) or (H, W, C)\n        params: Parameters for the noise distribution\n        max_value: Maximum value for the noise distribution\n        random_generator: NumPy random generator instance\n\n    Returns:\n        Noise array of shape (H, W) or (H, W, C) where the same noise\n        pattern is shared across all channels\n    \"\"\"\n    # Generate noise for (H, W)\n    height, width = shape[:2]\n    noise_map = sample_noise(\n        noise_type,\n        (height, width),\n        params,\n        max_value,\n        random_generator,\n    )\n\n    # If input is multichannel, broadcast noise to all channels\n    if len(shape) > MONO_CHANNEL_DIMENSIONS:\n        return np.broadcast_to(noise_map[..., None], shape)\n    return noise_map\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_snow_textures","title":"def generate_snow_textures (img_shape, random_generator) [view source on GitHub]","text":"

Generate snow texture and sparkle mask.

Parameters:

Name Type Description img_shape tuple[int, int]

Image shape.

random_generator np.random.Generator

Random generator to use.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

Tuple of (snow_texture, sparkle_mask) arrays.

Source code in albumentations/augmentations/functional.py Python
def generate_snow_textures(\n    img_shape: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate snow texture and sparkle mask.\n\n    Args:\n        img_shape (tuple[int, int]): Image shape.\n        random_generator (np.random.Generator): Random generator to use.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: Tuple of (snow_texture, sparkle_mask) arrays.\n    \"\"\"\n    # Generate base snow texture\n    snow_texture = random_generator.normal(size=img_shape[:2], loc=0.5, scale=0.3)\n    snow_texture = cv2.GaussianBlur(snow_texture, (0, 0), sigmaX=1, sigmaY=1)\n\n    # Generate sparkle mask\n    sparkle_mask = random_generator.random(img_shape[:2]) > 0.99\n\n    return snow_texture, sparkle_mask\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_fog_particle_radiuses","title":"def get_fog_particle_radiuses (img_shape, num_particles, fog_intensity, random_generator) [view source on GitHub]","text":"

Generate radiuses for fog particles.

Parameters:

Name Type Description img_shape tuple[int, int]

Image shape.

num_particles int

Number of fog particles.

fog_intensity float

Intensity of the fog effect, between 0 and 1.

random_generator np.random.Generator

Random generator to use.

Returns:

Type Description list[int]

List of radiuses for each fog particle.

Source code in albumentations/augmentations/functional.py Python
def get_fog_particle_radiuses(\n    img_shape: tuple[int, int],\n    num_particles: int,\n    fog_intensity: float,\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Generate radiuses for fog particles.\n\n    Args:\n        img_shape (tuple[int, int]): Image shape.\n        num_particles (int): Number of fog particles.\n        fog_intensity (float): Intensity of the fog effect, between 0 and 1.\n        random_generator (np.random.Generator): Random generator to use.\n\n    Returns:\n        list[int]: List of radiuses for each fog particle.\n    \"\"\"\n    height, width = img_shape[:2]\n    max_fog_radius = max(2, int(min(height, width) * 0.1 * fog_intensity))\n    min_radius = max(1, max_fog_radius // 2)\n\n    return [random_generator.integers(min_radius, max_fog_radius) for _ in range(num_particles)]\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_grid_size","title":"def get_grid_size (size, target_shape) [view source on GitHub]","text":"

Round up to nearest power of 2.

Source code in albumentations/augmentations/functional.py Python
def get_grid_size(size: int, target_shape: tuple[int, int]) -> int:\n    \"\"\"Round up to nearest power of 2.\"\"\"\n    return 2 ** int(np.ceil(np.log2(max(size, *target_shape))))\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_safe_brightness_contrast_params","title":"def get_safe_brightness_contrast_params (alpha, beta, max_value) [view source on GitHub]","text":"

Calculate safe alpha and beta values to prevent overflow/underflow.

For any pixel value x, we want: 0 <= alpha * x + beta <= max_value

Parameters:

Name Type Description alpha float

Contrast factor (1 means no change)

beta float

Brightness offset

max_value float

Maximum allowed value (255 for uint8, 1 for float32)

Returns:

Type Description tuple[float, float]

Safe (alpha, beta) values that prevent overflow/underflow

Source code in albumentations/augmentations/functional.py Python
def get_safe_brightness_contrast_params(\n    alpha: float,\n    beta: float,\n    max_value: float,\n) -> tuple[float, float]:\n    \"\"\"Calculate safe alpha and beta values to prevent overflow/underflow.\n\n    For any pixel value x, we want: 0 <= alpha * x + beta <= max_value\n\n    Args:\n        alpha: Contrast factor (1 means no change)\n        beta: Brightness offset\n        max_value: Maximum allowed value (255 for uint8, 1 for float32)\n\n    Returns:\n        tuple[float, float]: Safe (alpha, beta) values that prevent overflow/underflow\n    \"\"\"\n    if alpha > 0:\n        # For x = max_value: alpha * max_value + beta <= max_value\n        # For x = 0: beta >= 0\n        safe_beta = np.clip(beta, 0, max_value)\n        # From alpha * max_value + safe_beta <= max_value\n        safe_alpha = min(alpha, (max_value - safe_beta) / max_value)\n    else:\n        # For x = 0: beta <= max_value\n        # For x = max_value: alpha * max_value + beta >= 0\n        safe_beta = min(beta, max_value)\n        # From alpha * max_value + safe_beta >= 0\n        safe_alpha = max(alpha, -safe_beta / max_value)\n\n    return safe_alpha, safe_beta\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.grayscale_to_multichannel","title":"def grayscale_to_multichannel (grayscale_image, num_output_channels=3) [view source on GitHub]","text":"

Convert a grayscale image to a multi-channel image.

This function takes a 2D grayscale image or a 3D image with a single channel and converts it to a multi-channel image by repeating the grayscale data across the specified number of channels.

Parameters:

Name Type Description grayscale_image np.ndarray

Input grayscale image. Can be 2D (height, width) or 3D (height, width, 1).

num_output_channels int

Number of channels in the output image. Defaults to 3.

Returns:

Type Description np.ndarray

Multi-channel image with shape (height, width, num_channels)

Source code in albumentations/augmentations/functional.py Python
def grayscale_to_multichannel(\n    grayscale_image: np.ndarray,\n    num_output_channels: int = 3,\n) -> np.ndarray:\n    \"\"\"Convert a grayscale image to a multi-channel image.\n\n    This function takes a 2D grayscale image or a 3D image with a single channel\n    and converts it to a multi-channel image by repeating the grayscale data\n    across the specified number of channels.\n\n    Args:\n        grayscale_image (np.ndarray): Input grayscale image. Can be 2D (height, width)\n                                      or 3D (height, width, 1).\n        num_output_channels (int, optional): Number of channels in the output image. Defaults to 3.\n\n    Returns:\n        np.ndarray: Multi-channel image with shape (height, width, num_channels)\n    \"\"\"\n    # If output should be single channel, just squeeze and return\n    if num_output_channels == 1:\n        return grayscale_image\n\n    # For multi-channel output, squeeze and stack\n    squeezed = np.squeeze(grayscale_image)\n\n    return cv2.merge([squeezed] * num_output_channels)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.image_compression","title":"def image_compression (img, quality, image_type) [view source on GitHub]","text":"

Apply compression to image.

Parameters:

Name Type Description img np.ndarray

Input image

quality int

Compression quality (0-100)

image_type Literal['.jpg', '.webp']

Type of compression ('.jpg' or '.webp')

Returns:

Type Description np.ndarray

Compressed image with same number of channels as input

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef image_compression(\n    img: np.ndarray,\n    quality: int,\n    image_type: Literal[\".jpg\", \".webp\"],\n) -> np.ndarray:\n    \"\"\"Apply compression to image.\n\n    Args:\n        img: Input image\n        quality: Compression quality (0-100)\n        image_type: Type of compression ('.jpg' or '.webp')\n\n    Returns:\n        Compressed image with same number of channels as input\n    \"\"\"\n    quality_flag = cv2.IMWRITE_JPEG_QUALITY if image_type == \".jpg\" else cv2.IMWRITE_WEBP_QUALITY\n\n    num_channels = get_num_channels(img)\n\n    if num_channels == 1:\n        # For grayscale, ensure we read back as single channel\n        _, encoded_img = cv2.imencode(image_type, img, (int(quality_flag), quality))\n        decoded = cv2.imdecode(encoded_img, cv2.IMREAD_GRAYSCALE)\n        return decoded[..., np.newaxis]  # Add channel dimension back\n\n    if num_channels == NUM_RGB_CHANNELS:\n        # Standard RGB image\n        _, encoded_img = cv2.imencode(image_type, img, (int(quality_flag), quality))\n        return cv2.imdecode(encoded_img, cv2.IMREAD_UNCHANGED)\n\n    # For 2,4 or more channels, we need to handle alpha/extra channels separately\n    if num_channels == 2:\n        # For 2 channels, pad to 3 channels and take only first 2 after compression\n        padded = np.pad(img, ((0, 0), (0, 0), (0, 1)), mode=\"constant\")\n        _, encoded_bgr = cv2.imencode(image_type, padded, (int(quality_flag), quality))\n        decoded_bgr = cv2.imdecode(encoded_bgr, cv2.IMREAD_UNCHANGED)\n        return decoded_bgr[..., :2]\n\n    # Process first 3 channels together\n    bgr = img[..., :NUM_RGB_CHANNELS]\n    _, encoded_bgr = cv2.imencode(image_type, bgr, (int(quality_flag), quality))\n    decoded_bgr = cv2.imdecode(encoded_bgr, cv2.IMREAD_UNCHANGED)\n\n    if num_channels > NUM_RGB_CHANNELS:\n        # Process additional channels one by one\n        extra_channels = []\n        for i in range(NUM_RGB_CHANNELS, num_channels):\n            channel = img[..., i]\n            _, encoded = cv2.imencode(image_type, channel, (int(quality_flag), quality))\n            decoded = cv2.imdecode(encoded, cv2.IMREAD_GRAYSCALE)\n            if len(decoded.shape) == 2:\n                decoded = decoded[..., np.newaxis]\n            extra_channels.append(decoded)\n\n        # Combine BGR with extra channels\n        return np.dstack([decoded_bgr, *extra_channels])\n\n    return decoded_bgr\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.initialize_grid","title":"def initialize_grid (grid_size, random_generator) [view source on GitHub]","text":"

Initialize grid with random corners.

Source code in albumentations/augmentations/functional.py Python
def initialize_grid(\n    grid_size: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Initialize grid with random corners.\"\"\"\n    pattern = np.zeros((grid_size + 1, grid_size + 1), dtype=np.float32)\n    for corner in [(0, 0), (0, -1), (-1, 0), (-1, -1)]:\n        pattern[corner] = random_generator.random()\n    return pattern\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.iso_noise","title":"def iso_noise (image, color_shift, intensity, random_generator) [view source on GitHub]","text":"

Apply poisson noise to an image to simulate camera sensor noise.

Parameters:

Name Type Description image np.ndarray

Input image. Currently, only RGB images are supported.

color_shift float

The amount of color shift to apply.

intensity float

Multiplication factor for noise values. Values of ~0.5 produce a noticeable, yet acceptable level of noise.

random_generator np.random.Generator

If specified, this will be random generator used for noise generation.

Returns:

Type Description np.ndarray

The noised image.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
@float32_io\n@clipped\ndef iso_noise(\n    image: np.ndarray,\n    color_shift: float,\n    intensity: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply poisson noise to an image to simulate camera sensor noise.\n\n    Args:\n        image (np.ndarray): Input image. Currently, only RGB images are supported.\n        color_shift (float): The amount of color shift to apply.\n        intensity (float): Multiplication factor for noise values. Values of ~0.5 produce a noticeable,\n                           yet acceptable level of noise.\n        random_generator (np.random.Generator): If specified, this will be random generator used\n            for noise generation.\n\n    Returns:\n        np.ndarray: The noised image.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n    _, stddev = cv2.meanStdDev(hls)\n\n    luminance_noise = random_generator.poisson(\n        stddev[1] * intensity,\n        size=hls.shape[:2],\n    )\n    color_noise = random_generator.normal(\n        0,\n        color_shift * intensity,\n        size=hls.shape[:2],\n    )\n\n    hls[..., 0] += color_noise\n    hls[..., 1] = add_array(\n        hls[..., 1],\n        luminance_noise * intensity * (1.0 - hls[..., 1]),\n    )\n\n    noised_hls = cv2.cvtColor(hls, cv2.COLOR_HLS2RGB)\n    return np.clip(noised_hls, 0, 1, out=noised_hls)  # Ensure output is in [0, 1] range\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.move_tone_curve","title":"def move_tone_curve (img, low_y, high_y) [view source on GitHub]","text":"

Rescales the relationship between bright and dark areas of the image by manipulating its tone curve.

Parameters:

Name Type Description img np.ndarray

np.ndarray. Any number of channels

low_y float | np.ndarray

per-channel or single y-position of a Bezier control point used to adjust the tone curve, must be in range [0, 1]

high_y float | np.ndarray

per-channel or single y-position of a Bezier control point used to adjust image tone curve, must be in range [0, 1]

Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef move_tone_curve(\n    img: np.ndarray,\n    low_y: float | np.ndarray,\n    high_y: float | np.ndarray,\n) -> np.ndarray:\n    \"\"\"Rescales the relationship between bright and dark areas of the image by manipulating its tone curve.\n\n    Args:\n        img: np.ndarray. Any number of channels\n        low_y: per-channel or single y-position of a Bezier control point used\n            to adjust the tone curve, must be in range [0, 1]\n        high_y: per-channel or single y-position of a Bezier control point used\n            to adjust image tone curve, must be in range [0, 1]\n\n    \"\"\"\n    t = np.linspace(0.0, 1.0, 256)\n\n    def evaluate_bez(\n        t: np.ndarray,\n        low_y: float | np.ndarray,\n        high_y: float | np.ndarray,\n    ) -> np.ndarray:\n        one_minus_t = 1 - t\n        return (3 * one_minus_t**2 * t * low_y + 3 * one_minus_t * t**2 * high_y + t**3) * 255\n\n    num_channels = get_num_channels(img)\n\n    if np.isscalar(low_y) and np.isscalar(high_y):\n        lut = clip(np.rint(evaluate_bez(t, low_y, high_y)), np.uint8, inplace=False)\n        return sz_lut(img, lut, inplace=False)\n    if isinstance(low_y, np.ndarray) and isinstance(high_y, np.ndarray):\n        luts = clip(\n            np.rint(evaluate_bez(t[:, np.newaxis], low_y, high_y).T),\n            np.uint8,\n            inplace=False,\n        )\n        return cv2.merge(\n            [sz_lut(img[:, :, i], np.ascontiguousarray(luts[i]), inplace=False) for i in range(num_channels)],\n        )\n\n    raise TypeError(\n        f\"low_y and high_y must both be of type float or np.ndarray. Got {type(low_y)} and {type(high_y)}\",\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.posterize","title":"def posterize (img, bits) [view source on GitHub]","text":"

Reduce the number of bits for each color channel by keeping only the highest N bits.

This transform performs bit-depth reduction by masking out lower bits, effectively reducing the number of possible values per channel. This creates a posterization effect where similar colors are merged together.

Parameters:

Name Type Description img np.ndarray

Input image. Can be single or multi-channel.

bits Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]]

Number of high bits to keep. Must be in range [1, 7]. Can be either: - A single value to apply the same bit reduction to all channels - A list of values to apply different bit reduction per channel. Length of list must match number of channels in image.

Returns:

Type Description np.ndarray

Image with reduced bit depth. Has same shape and dtype as input.

Note

  • The transform keeps the N highest bits and sets all other bits to 0
  • For example, if bits=3:
    • Original value: 11010110 (214)
    • Keep 3 bits: 11000000 (192)
  • The number of unique colors per channel will be 2^bits
  • Higher bits values = more colors = more subtle effect
  • Lower bits values = fewer colors = more dramatic posterization

Examples:

Python
>>> import numpy as np\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Same posterization for all channels\n>>> result = posterize(image, bits=3)\n>>> # Different posterization per channel\n>>> result = posterize(image, bits=[3, 4, 5])  # RGB channels\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef posterize(img: np.ndarray, bits: Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]]) -> np.ndarray:\n    \"\"\"Reduce the number of bits for each color channel by keeping only the highest N bits.\n\n    This transform performs bit-depth reduction by masking out lower bits, effectively\n    reducing the number of possible values per channel. This creates a posterization\n    effect where similar colors are merged together.\n\n    Args:\n        img: Input image. Can be single or multi-channel.\n        bits: Number of high bits to keep. Must be in range [1, 7].\n            Can be either:\n            - A single value to apply the same bit reduction to all channels\n            - A list of values to apply different bit reduction per channel.\n              Length of list must match number of channels in image.\n\n    Returns:\n        np.ndarray: Image with reduced bit depth. Has same shape and dtype as input.\n\n    Note:\n        - The transform keeps the N highest bits and sets all other bits to 0\n        - For example, if bits=3:\n            - Original value: 11010110 (214)\n            - Keep 3 bits:   11000000 (192)\n        - The number of unique colors per channel will be 2^bits\n        - Higher bits values = more colors = more subtle effect\n        - Lower bits values = fewer colors = more dramatic posterization\n\n    Examples:\n        >>> import numpy as np\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Same posterization for all channels\n        >>> result = posterize(image, bits=3)\n        >>> # Different posterization per channel\n        >>> result = posterize(image, bits=[3, 4, 5])  # RGB channels\n    \"\"\"\n    bits_array = np.uint8(bits)\n\n    if not bits_array.shape or len(bits_array) == 1:\n        lut = np.arange(0, 256, dtype=np.uint8)\n        mask = ~np.uint8(2 ** (8 - bits_array) - 1)\n        lut &= mask\n\n        return sz_lut(img, lut, inplace=False)\n\n    result_img = np.empty_like(img)\n    for i, channel_bits in enumerate(bits_array):\n        lut = np.arange(0, 256, dtype=np.uint8)\n        mask = ~np.uint8(2 ** (8 - channel_bits) - 1)\n        lut &= mask\n\n        result_img[..., i] = sz_lut(img[..., i], lut, inplace=True)\n\n    return result_img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.prepare_illumination_input","title":"def prepare_illumination_input (img) [view source on GitHub]","text":"

Prepare image for illumination effect.

Parameters:

Name Type Description img np.ndarray

Input image

Returns:

Type Description tuple of
  • float32 image
  • height
  • width
Source code in albumentations/augmentations/functional.py Python
def prepare_illumination_input(img: np.ndarray) -> tuple[np.ndarray, int, int]:\n    \"\"\"Prepare image for illumination effect.\n\n    Args:\n        img: Input image\n\n    Returns:\n        tuple of:\n        - float32 image\n        - height\n        - width\n    \"\"\"\n    result = img.astype(np.float32)\n    height, width = img.shape[:2]\n    return result, height, width\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.random_offset","title":"def random_offset (current_size, total_size, roughness, random_generator) [view source on GitHub]","text":"

Calculate random offset based on current grid size.

Source code in albumentations/augmentations/functional.py Python
def random_offset(\n    current_size: int,\n    total_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Calculate random offset based on current grid size.\"\"\"\n    return (random_generator.random() - 0.5) * (current_size / total_size) ** (roughness / 2)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_beta","title":"def sample_beta (size, params, random_generator) [view source on GitHub]","text":"

Sample from Beta distribution.

The Beta distribution is bounded by [0, 1] and then scaled and shifted to [-scale, scale]. Alpha and beta parameters control the shape of the distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_beta(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Beta distribution.\n\n    The Beta distribution is bounded by [0, 1] and then scaled and shifted to [-scale, scale].\n    Alpha and beta parameters control the shape of the distribution.\n    \"\"\"\n    alpha = random_generator.uniform(*params[\"alpha_range\"])\n    beta = random_generator.uniform(*params[\"beta_range\"])\n    scale = random_generator.uniform(*params[\"scale_range\"])\n\n    # Sample from Beta[0,1] and transform to [-scale,scale]\n    samples = random_generator.beta(alpha, beta, size=size)\n    return (2 * samples - 1) * scale\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_gaussian","title":"def sample_gaussian (size, params, random_generator) [view source on GitHub]","text":"

Sample from Gaussian distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_gaussian(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Gaussian distribution.\"\"\"\n    mean = random_generator.uniform(*params[\"mean_range\"])\n    std = random_generator.uniform(*params[\"std_range\"])\n    return random_generator.normal(mean, std, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_laplace","title":"def sample_laplace (size, params, random_generator) [view source on GitHub]","text":"

Sample from Laplace distribution.

The Laplace distribution is also known as the double exponential distribution. It has heavier tails than the Gaussian distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_laplace(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Laplace distribution.\n\n    The Laplace distribution is also known as the double exponential distribution.\n    It has heavier tails than the Gaussian distribution.\n    \"\"\"\n    loc = random_generator.uniform(*params[\"mean_range\"])\n    scale = random_generator.uniform(*params[\"scale_range\"])\n    return random_generator.laplace(loc=loc, scale=scale, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_noise","title":"def sample_noise (noise_type, size, params, max_value, random_generator) [view source on GitHub]","text":"

Sample from specific noise distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from specific noise distribution.\"\"\"\n    if noise_type == \"uniform\":\n        return sample_uniform(size, params, random_generator) * max_value\n    if noise_type == \"gaussian\":\n        return sample_gaussian(size, params, random_generator) * max_value\n    if noise_type == \"laplace\":\n        return sample_laplace(size, params, random_generator) * max_value\n    if noise_type == \"beta\":\n        return sample_beta(size, params, random_generator) * max_value\n\n    raise ValueError(f\"Unknown noise type: {noise_type}\")\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_uniform","title":"def sample_uniform (size, params, random_generator) [view source on GitHub]","text":"

Sample from uniform distribution.

Parameters:

Name Type Description size tuple[int, ...]

Output shape. If length is 1, generates constant noise per channel.

params dict[str, Any]

Must contain 'ranges' key with list of (min, max) tuples. If only one range is provided, it will be used for all channels.

random_generator np.random.Generator

NumPy random generator instance

Returns:

Type Description np.ndarray | float

Noise array of specified size. For single-channel constant mode, returns scalar instead of array with shape (1,).

Source code in albumentations/augmentations/functional.py Python
def sample_uniform(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray | float:\n    \"\"\"Sample from uniform distribution.\n\n    Args:\n        size: Output shape. If length is 1, generates constant noise per channel.\n        params: Must contain 'ranges' key with list of (min, max) tuples.\n            If only one range is provided, it will be used for all channels.\n        random_generator: NumPy random generator instance\n\n    Returns:\n        Noise array of specified size. For single-channel constant mode,\n        returns scalar instead of array with shape (1,).\n    \"\"\"\n    if len(size) == 1:  # constant mode\n        ranges = params[\"ranges\"]\n        num_channels = size[0]\n\n        if len(ranges) == 1:\n            ranges = ranges * num_channels\n        elif len(ranges) < num_channels:\n            raise ValueError(\n                f\"Not enough ranges provided. Expected {num_channels}, got {len(ranges)}\",\n            )\n\n        return np.array(\n            [random_generator.uniform(low, high) for low, high in ranges[:num_channels]],\n        )\n\n    # use first range for spatial noise\n    low, high = params[\"ranges\"][0]\n    return random_generator.uniform(low, high, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sharpen_gaussian","title":"def sharpen_gaussian (img, alpha, kernel_size, sigma) [view source on GitHub]","text":"

Sharpen image using Gaussian blur.

Source code in albumentations/augmentations/functional.py Python
@clipped\n@preserve_channel_dim\ndef sharpen_gaussian(\n    img: np.ndarray,\n    alpha: float,\n    kernel_size: int,\n    sigma: float,\n) -> np.ndarray:\n    \"\"\"Sharpen image using Gaussian blur.\"\"\"\n    blurred = cv2.GaussianBlur(\n        img,\n        ksize=(kernel_size, kernel_size),\n        sigmaX=sigma,\n        sigmaY=sigma,\n    )\n    # Unsharp mask formula: original + alpha * (original - blurred)\n    # This is equivalent to: original * (1 + alpha) - alpha * blurred\n    return img + alpha * (img - blurred)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.shot_noise","title":"def shot_noise (img, scale, random_generator) [view source on GitHub]","text":"

Apply shot noise to the image by simulating photon counting in linear light space.

This function simulates photon shot noise, which occurs due to the quantum nature of light. The process: 1. Converts image to linear light space (removes gamma correction) 2. Scales pixel values to represent expected photon counts 3. Samples actual photon counts from Poisson distribution 4. Converts back to display space (reapplies gamma)

The simulation is performed in linear light space because photon shot noise is a physical process that occurs before gamma correction is applied by cameras/displays.

Parameters:

Name Type Description img np.ndarray

Input image in range [0, 1]. Can be single or multi-channel.

scale float

Reciprocal of the number of photons (noise intensity). - Larger values = fewer photons = more noise - Smaller values = more photons = less noise For example: - scale = 0.1 simulates ~100 photons per unit intensity - scale = 10.0 simulates ~0.1 photons per unit intensity

random_generator np.random.Generator

NumPy random generator for Poisson sampling

Returns:

Type Description Image with shot noise applied, same shape and range [0, 1] as input. The noise characteristics will follow Poisson statistics in linear space
  • Variance equals mean in linear space
  • More noise in brighter regions (but less relative noise)
  • Less noise in darker regions (but more relative noise)

Note

  • Uses gamma value of 2.2 for linear/display space conversion
  • Adds small constant (1e-6) to avoid issues with zero values
  • Clips final values to [0, 1] range
  • Operates on the image in-place for memory efficiency
  • Preserves float32 precision throughout calculations

References

  • https://en.wikipedia.org/wiki/Shot_noise
  • https://en.wikipedia.org/wiki/Gamma_correction
Source code in albumentations/augmentations/functional.py Python
@preserve_channel_dim\n@float32_io\ndef shot_noise(\n    img: np.ndarray,\n    scale: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply shot noise to the image by simulating photon counting in linear light space.\n\n    This function simulates photon shot noise, which occurs due to the quantum nature of light.\n    The process:\n    1. Converts image to linear light space (removes gamma correction)\n    2. Scales pixel values to represent expected photon counts\n    3. Samples actual photon counts from Poisson distribution\n    4. Converts back to display space (reapplies gamma)\n\n    The simulation is performed in linear light space because photon shot noise is a physical\n    process that occurs before gamma correction is applied by cameras/displays.\n\n    Args:\n        img: Input image in range [0, 1]. Can be single or multi-channel.\n        scale: Reciprocal of the number of photons (noise intensity).\n            - Larger values = fewer photons = more noise\n            - Smaller values = more photons = less noise\n            For example:\n            - scale = 0.1 simulates ~100 photons per unit intensity\n            - scale = 10.0 simulates ~0.1 photons per unit intensity\n        random_generator: NumPy random generator for Poisson sampling\n\n    Returns:\n        Image with shot noise applied, same shape and range [0, 1] as input.\n        The noise characteristics will follow Poisson statistics in linear space:\n        - Variance equals mean in linear space\n        - More noise in brighter regions (but less relative noise)\n        - Less noise in darker regions (but more relative noise)\n\n    Note:\n        - Uses gamma value of 2.2 for linear/display space conversion\n        - Adds small constant (1e-6) to avoid issues with zero values\n        - Clips final values to [0, 1] range\n        - Operates on the image in-place for memory efficiency\n        - Preserves float32 precision throughout calculations\n\n    References:\n        - https://en.wikipedia.org/wiki/Shot_noise\n        - https://en.wikipedia.org/wiki/Gamma_correction\n    \"\"\"\n    # Apply inverse gamma correction to work in linear space\n    img_linear = cv2.pow(img, 2.2)\n\n    # Scale image values and add small constant to avoid zero values\n    scaled_img = (img_linear + scale * 1e-6) / scale\n\n    # Generate Poisson noise\n    noisy_img = multiply_by_constant(\n        random_generator.poisson(scaled_img).astype(np.float32),\n        scale,\n        inplace=True,\n    )\n\n    # Scale back and apply gamma correction\n    return power(np.clip(noisy_img, 0, 1, out=noisy_img), 1 / 2.2)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.slic","title":"def slic (image, n_segments, compactness=10.0, max_iterations=10) [view source on GitHub]","text":"

Simple Linear Iterative Clustering (SLIC) superpixel segmentation using OpenCV and NumPy.

Parameters:

Name Type Description image np.ndarray

Input image (2D or 3D numpy array).

n_segments int

Approximate number of superpixels to generate.

compactness float

Balance between color proximity and space proximity.

max_iterations int

Maximum number of iterations for k-means.

Returns:

Type Description np.ndarray

Segmentation mask where each superpixel has a unique label.

Source code in albumentations/augmentations/functional.py Python
def slic(\n    image: np.ndarray,\n    n_segments: int,\n    compactness: float = 10.0,\n    max_iterations: int = 10,\n) -> np.ndarray:\n    \"\"\"Simple Linear Iterative Clustering (SLIC) superpixel segmentation using OpenCV and NumPy.\n\n    Args:\n        image (np.ndarray): Input image (2D or 3D numpy array).\n        n_segments (int): Approximate number of superpixels to generate.\n        compactness (float): Balance between color proximity and space proximity.\n        max_iterations (int): Maximum number of iterations for k-means.\n\n    Returns:\n        np.ndarray: Segmentation mask where each superpixel has a unique label.\n    \"\"\"\n    if image.ndim == MONO_CHANNEL_DIMENSIONS:\n        image = image[..., np.newaxis]\n\n    height, width = image.shape[:2]\n    num_pixels = height * width\n\n    # Normalize image to [0, 1] range\n    image_normalized = image.astype(np.float32) / np.max(image + 1e-6)\n\n    # Initialize cluster centers\n    grid_step = int((num_pixels / n_segments) ** 0.5)\n    x_range = np.arange(grid_step // 2, width, grid_step)\n    y_range = np.arange(grid_step // 2, height, grid_step)\n    centers = np.array(\n        [(x, y) for y in y_range for x in x_range if x < width and y < height],\n    )\n\n    # Initialize labels and distances\n    labels = -1 * np.ones((height, width), dtype=np.int32)\n    distances = np.full((height, width), np.inf)\n\n    for _ in range(max_iterations):\n        for i, center in enumerate(centers):\n            y, x = int(center[1]), int(center[0])\n\n            # Define the neighborhood\n            y_low, y_high = max(0, y - grid_step), min(height, y + grid_step + 1)\n            x_low, x_high = max(0, x - grid_step), min(width, x + grid_step + 1)\n\n            # Compute distances\n            crop = image_normalized[y_low:y_high, x_low:x_high]\n            color_diff = crop - image_normalized[y, x]\n            color_distance = np.sum(color_diff**2, axis=-1)\n\n            yy, xx = np.ogrid[y_low:y_high, x_low:x_high]\n            spatial_distance = ((yy - y) ** 2 + (xx - x) ** 2) / (grid_step**2)\n\n            distance = color_distance + compactness * spatial_distance\n\n            mask = distance < distances[y_low:y_high, x_low:x_high]\n            distances[y_low:y_high, x_low:x_high][mask] = distance[mask]\n            labels[y_low:y_high, x_low:x_high][mask] = i\n\n        # Update centers\n        for i in range(len(centers)):\n            mask = labels == i\n            if np.any(mask):\n                centers[i] = np.mean(np.argwhere(mask), axis=0)[::-1]\n\n    return labels\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.solarize","title":"def solarize (img, threshold) [view source on GitHub]","text":"

Invert all pixel values above a threshold.

Parameters:

Name Type Description img np.ndarray

The image to solarize. Can be uint8 or float32.

threshold float

Normalized threshold value in range [0, 1]. For uint8 images: pixels above threshold * 255 are inverted For float32 images: pixels above threshold are inverted

Returns:

Type Description np.ndarray

Solarized image.

Note

The threshold is normalized to [0, 1] range for both uint8 and float32 images. For uint8 images, the threshold is internally scaled by 255.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef solarize(img: np.ndarray, threshold: float) -> np.ndarray:\n    \"\"\"Invert all pixel values above a threshold.\n\n    Args:\n        img: The image to solarize. Can be uint8 or float32.\n        threshold: Normalized threshold value in range [0, 1].\n            For uint8 images: pixels above threshold * 255 are inverted\n            For float32 images: pixels above threshold are inverted\n\n    Returns:\n        Solarized image.\n\n    Note:\n        The threshold is normalized to [0, 1] range for both uint8 and float32 images.\n        For uint8 images, the threshold is internally scaled by 255.\n    \"\"\"\n    dtype = img.dtype\n    max_val = MAX_VALUES_BY_DTYPE[dtype]\n\n    if dtype == np.uint8:\n        lut = [(max_val - i if i >= threshold * max_val else i) for i in range(int(max_val) + 1)]\n\n        prev_shape = img.shape\n        img = sz_lut(img, np.array(lut, dtype=dtype), inplace=False)\n\n        return np.expand_dims(img, -1) if len(prev_shape) != img.ndim else img\n\n    img = img.copy()\n\n    cond = img >= threshold\n    img[cond] = max_val - img[cond]\n    return img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.square_step","title":"def square_step (pattern, y, x, step, grid_size, roughness, random_generator) [view source on GitHub]","text":"

Compute center value during square step.

Source code in albumentations/augmentations/functional.py Python
def square_step(\n    pattern: np.ndarray,\n    y: int,\n    x: int,\n    step: int,\n    grid_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Compute center value during square step.\"\"\"\n    corners = [\n        pattern[y, x],  # top-left\n        pattern[y, x + step],  # top-right\n        pattern[y + step, x],  # bottom-left\n        pattern[y + step, x + step],  # bottom-right\n    ]\n    return sum(corners) / 4.0 + random_offset(\n        step,\n        grid_size,\n        roughness,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_average","title":"def to_gray_average (img) [view source on GitHub]","text":"

Convert an image to grayscale using the average method.

This function computes the arithmetic mean across all channels for each pixel, resulting in a grayscale representation of the image.

Key aspects of this method: 1. It treats all channels equally, regardless of their perceptual importance. 2. Works with any number of channels, making it versatile for various image types. 3. Simple and fast to compute, but may not accurately represent perceived brightness. 4. For RGB images, the formula is: Gray = (R + G + B) / 3

Note: This method may produce different results compared to weighted methods (like RGB weighted average) which account for human perception of color brightness. It may also produce unexpected results for images with alpha channels or non-color data in additional channels.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array. Can be any number of channels.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array. The output data type matches the input data type.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
def to_gray_average(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the average method.\n\n    This function computes the arithmetic mean across all channels for each pixel,\n    resulting in a grayscale representation of the image.\n\n    Key aspects of this method:\n    1. It treats all channels equally, regardless of their perceptual importance.\n    2. Works with any number of channels, making it versatile for various image types.\n    3. Simple and fast to compute, but may not accurately represent perceived brightness.\n    4. For RGB images, the formula is: Gray = (R + G + B) / 3\n\n    Note: This method may produce different results compared to weighted methods\n    (like RGB weighted average) which account for human perception of color brightness.\n    It may also produce unexpected results for images with alpha channels or\n    non-color data in additional channels.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array. Can be any number of channels.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array. The output data type\n                    matches the input data type.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    return np.mean(img, axis=-1).astype(img.dtype)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_desaturation","title":"def to_gray_desaturation (img) [view source on GitHub]","text":"

Convert an image to grayscale using the desaturation method.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef to_gray_desaturation(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the desaturation method.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    float_image = img.astype(np.float32)\n    return (np.max(float_image, axis=-1) + np.min(float_image, axis=-1)) / 2\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_from_lab","title":"def to_gray_from_lab (img) [view source on GitHub]","text":"

Convert an RGB image to grayscale using the L channel from the LAB color space.

This function converts the RGB image to the LAB color space and extracts the L channel. The LAB color space is designed to approximate human vision, where L represents lightness.

Key aspects of this method: 1. The L channel represents the lightness of each pixel, ranging from 0 (black) to 100 (white). 2. It's more perceptually uniform than RGB, meaning equal changes in L values correspond to roughly equal changes in perceived lightness. 3. The L channel is independent of the color information (A and B channels), making it suitable for grayscale conversion.

This method can be particularly useful when you want a grayscale image that closely matches human perception of lightness, potentially preserving more perceived contrast than simple RGB-based methods.

Parameters:

Name Type Description img np.ndarray

Input RGB image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array, representing the L (lightness) channel. Values are scaled to match the input image's data type range.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef to_gray_from_lab(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an RGB image to grayscale using the L channel from the LAB color space.\n\n    This function converts the RGB image to the LAB color space and extracts the L channel.\n    The LAB color space is designed to approximate human vision, where L represents lightness.\n\n    Key aspects of this method:\n    1. The L channel represents the lightness of each pixel, ranging from 0 (black) to 100 (white).\n    2. It's more perceptually uniform than RGB, meaning equal changes in L values correspond to\n       roughly equal changes in perceived lightness.\n    3. The L channel is independent of the color information (A and B channels), making it\n       suitable for grayscale conversion.\n\n    This method can be particularly useful when you want a grayscale image that closely\n    matches human perception of lightness, potentially preserving more perceived contrast\n    than simple RGB-based methods.\n\n    Args:\n        img (np.ndarray): Input RGB image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array, representing the L (lightness) channel.\n                    Values are scaled to match the input image's data type range.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    return cv2.cvtColor(img, cv2.COLOR_RGB2LAB)[..., 0]\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_max","title":"def to_gray_max (img) [view source on GitHub]","text":"

Convert an image to grayscale using the maximum channel value method.

This function takes the maximum value across all channels for each pixel, resulting in a grayscale image that preserves the brightest parts of the original image.

Key aspects of this method: 1. Works with any number of channels, making it versatile for various image types. 2. For 3-channel (e.g., RGB) images, this method is equivalent to extracting the V (Value) channel from the HSV color space. 3. Preserves the brightest parts of the image but may lose some color contrast information. 4. Simple and fast to compute.

Note: - This method tends to produce brighter grayscale images compared to other conversion methods, as it always selects the highest intensity value from the channels. - For RGB images, it may not accurately represent perceived brightness as it doesn't account for human color perception.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array. Can be any number of channels.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array. The output data type matches the input data type.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
def to_gray_max(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the maximum channel value method.\n\n    This function takes the maximum value across all channels for each pixel,\n    resulting in a grayscale image that preserves the brightest parts of the original image.\n\n    Key aspects of this method:\n    1. Works with any number of channels, making it versatile for various image types.\n    2. For 3-channel (e.g., RGB) images, this method is equivalent to extracting the V (Value)\n       channel from the HSV color space.\n    3. Preserves the brightest parts of the image but may lose some color contrast information.\n    4. Simple and fast to compute.\n\n    Note:\n    - This method tends to produce brighter grayscale images compared to other conversion methods,\n      as it always selects the highest intensity value from the channels.\n    - For RGB images, it may not accurately represent perceived brightness as it doesn't\n      account for human color perception.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array. Can be any number of channels.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array. The output data type\n                    matches the input data type.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    return np.max(img, axis=-1)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_pca","title":"def to_gray_pca (img) [view source on GitHub]","text":"

Convert an image to grayscale using Principal Component Analysis (PCA).

This function applies PCA to reduce a multi-channel image to a single channel, effectively creating a grayscale representation that captures the maximum variance in the color data.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array with shape (height, width, channels).

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array with shape (height, width). If input is uint8, output is uint8 in range [0, 255]. If input is float32, output is float32 in range [0, 1].

Note

This method can potentially preserve more information from the original image compared to standard weighted average methods, as it accounts for the correlations between color channels.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef to_gray_pca(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using Principal Component Analysis (PCA).\n\n    This function applies PCA to reduce a multi-channel image to a single channel,\n    effectively creating a grayscale representation that captures the maximum variance\n    in the color data.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array with shape (height, width, channels).\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array with shape (height, width).\n                    If input is uint8, output is uint8 in range [0, 255].\n                    If input is float32, output is float32 in range [0, 1].\n\n    Note:\n        This method can potentially preserve more information from the original image\n        compared to standard weighted average methods, as it accounts for the\n        correlations between color channels.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    dtype = img.dtype\n    # Reshape the image to a 2D array of pixels\n    pixels = img.reshape(-1, img.shape[2])\n\n    # Perform PCA\n    pca = PCA(n_components=1)\n    pca_result = pca.fit_transform(pixels)\n\n    # Reshape back to image dimensions and scale to 0-255\n    grayscale = pca_result.reshape(img.shape[:2])\n    grayscale = normalize_per_image(grayscale, \"min_max\")\n\n    return from_float(grayscale, target_dtype=dtype) if dtype == np.uint8 else grayscale\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_weighted_average","title":"def to_gray_weighted_average (img) [view source on GitHub]","text":"

Convert an RGB image to grayscale using the weighted average method.

This function uses OpenCV's cvtColor function with COLOR_RGB2GRAY conversion, which applies the following formula: Y = 0.299R + 0.587G + 0.114*B

Parameters:

Name Type Description img np.ndarray

Input RGB image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
def to_gray_weighted_average(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an RGB image to grayscale using the weighted average method.\n\n    This function uses OpenCV's cvtColor function with COLOR_RGB2GRAY conversion,\n    which applies the following formula:\n    Y = 0.299*R + 0.587*G + 0.114*B\n\n    Args:\n        img (np.ndarray): Input RGB image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n
"},{"location":"api_reference/augmentations/geometric/","title":"Geometric augmentations (augmentations.geometric)","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional","title":"functional","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.adjust_padding_by_position","title":"def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py Python
def adjust_padding_by_position(\n    h_top: int,\n    h_bottom: int,\n    w_left: int,\n    w_right: int,\n    position: PositionType,\n    py_random: np.random.RandomState,\n) -> tuple[int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position.\"\"\"\n    if position == \"center\":\n        return h_top, h_bottom, w_left, w_right\n\n    if position == \"top_left\":\n        return 0, h_top + h_bottom, 0, w_left + w_right\n\n    if position == \"top_right\":\n        return 0, h_top + h_bottom, w_left + w_right, 0\n\n    if position == \"bottom_left\":\n        return h_top + h_bottom, 0, 0, w_left + w_right\n\n    if position == \"bottom_right\":\n        return h_top + h_bottom, 0, w_left + w_right, 0\n\n    if position == \"random\":\n        h_pad = h_top + h_bottom\n        w_pad = w_left + w_right\n        h_top = py_random.randint(0, h_pad)\n        h_bottom = h_pad - h_top\n        w_left = py_random.randint(0, w_pad)\n        w_right = w_pad - w_left\n        return h_top, h_bottom, w_left, w_right\n\n    raise ValueError(f\"Unknown position: {position}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.almost_equal_intervals","title":"def almost_equal_intervals (n, parts) [view source on GitHub]","text":"

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)\narray([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n>>> almost_equal_intervals(16, 4)\narray([4, 4, 4, 4])  # Splits 16 into four equal parts\n
Source code in albumentations/augmentations/geometric/functional.py Python
def almost_equal_intervals(n: int, parts: int) -> np.ndarray:\n    \"\"\"Generates an array of nearly equal integer intervals that sum up to `n`.\n\n    This function divides the number `n` into `parts` nearly equal parts. It ensures that\n    the sum of all parts equals `n`, and the difference between any two parts is at most one.\n    This is useful for distributing a total amount into nearly equal discrete parts.\n\n    Args:\n        n (int): The total value to be split.\n        parts (int): The number of parts to split into.\n\n    Returns:\n        np.ndarray: An array of integers where each integer represents the size of a part.\n\n    Example:\n        >>> almost_equal_intervals(20, 3)\n        array([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n        >>> almost_equal_intervals(16, 4)\n        array([4, 4, 4, 4])  # Splits 16 into four equal parts\n    \"\"\"\n    part_size, remainder = divmod(n, parts)\n    # Create an array with the base part size and adjust the first `remainder` parts by adding 1\n    return np.array(\n        [part_size + 1 if i < remainder else part_size for i in range(parts)],\n    )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.apply_affine_to_points","title":"def apply_affine_to_points (points, matrix) [view source on GitHub]","text":"

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"points\")\ndef apply_affine_to_points(points: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply affine transformation to a set of points.\n\n    This function handles potential division by zero by replacing zero values\n    in the homogeneous coordinate with a small epsilon value.\n\n    Args:\n        points (np.ndarray): Array of points with shape (N, 2).\n        matrix (np.ndarray): 3x3 affine transformation matrix.\n\n    Returns:\n        np.ndarray: Transformed points with shape (N, 2).\n    \"\"\"\n    homogeneous_points = np.column_stack([points, np.ones(points.shape[0])])\n    transformed_points = homogeneous_points @ matrix.T\n\n    # Handle potential division by zero\n    epsilon = np.finfo(transformed_points.dtype).eps\n    transformed_points[:, 2] = np.where(\n        np.abs(transformed_points[:, 2]) < epsilon,\n        np.sign(transformed_points[:, 2]) * epsilon,\n        transformed_points[:, 2],\n    )\n\n    return transformed_points[:, :2] / transformed_points[:, 2:]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine","title":"def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine(\n    bboxes: np.ndarray,\n    matrix: np.ndarray,\n    rotate_method: Literal[\"largest_box\", \"ellipse\"],\n    image_shape: tuple[int, int],\n    border_mode: int,\n    output_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes.\n\n    For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function:\n    1. Calculates necessary padding to avoid information loss\n    2. Applies padding to the bounding boxes\n    3. Adjusts the transformation matrix to account for padding\n    4. Applies the affine transformation\n    5. Validates the transformed bounding boxes\n\n    For other border modes, it directly applies the affine transformation without padding.\n\n    Args:\n        bboxes (np.ndarray): Input bounding boxes\n        matrix (np.ndarray): Affine transformation matrix\n        rotate_method (str): Method for rotating bounding boxes ('largest_box' or 'ellipse')\n        image_shape (Sequence[int]): Shape of the input image\n        border_mode (int): OpenCV border mode\n        output_shape (Sequence[int]): Shape of the output image\n\n    Returns:\n        np.ndarray: Transformed and normalized bounding boxes\n    \"\"\"\n    if is_identity_matrix(matrix):\n        return bboxes\n\n    bboxes = denormalize_bboxes(bboxes, image_shape)\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        bboxes = generate_reflected_bboxes(\n            bboxes,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Apply affine transform\n    if rotate_method == \"largest_box\":\n        transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n    elif rotate_method == \"ellipse\":\n        transformed_bboxes = bboxes_affine_ellipse(bboxes, matrix)\n    else:\n        raise ValueError(f\"Method {rotate_method} is not a valid rotation method.\")\n\n    # Validate and normalize bboxes\n    validated_bboxes = validate_bboxes(transformed_bboxes, output_shape)\n\n    return normalize_bboxes(validated_bboxes, output_shape)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine_ellipse","title":"def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_ellipse(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes using an ellipse approximation method.\n\n    This function transforms bounding boxes by approximating each box with an ellipse,\n    transforming points along the ellipse's circumference, and then computing the\n    new bounding box that encloses the transformed ellipse.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The ellipse approximation method can provide a tighter bounding box compared to the\n          largest box method, especially for rotations.\n        - 360 points are used to approximate each ellipse, which provides a good balance between\n          accuracy and computational efficiency.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method may be more suitable for objects that are roughly elliptical in shape.\n    \"\"\"\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n    bbox_width = (x_max - x_min) / 2\n    bbox_height = (y_max - y_min) / 2\n    center_x = x_min + bbox_width\n    center_y = y_min + bbox_height\n\n    angles = np.arange(0, 360, dtype=np.float32)\n    cos_angles = np.cos(np.radians(angles))\n    sin_angles = np.sin(np.radians(angles))\n\n    # Generate points for all ellipses at once\n    x = bbox_width[:, np.newaxis] * sin_angles + center_x[:, np.newaxis]\n    y = bbox_height[:, np.newaxis] * cos_angles + center_y[:, np.newaxis]\n    points = np.stack([x, y], axis=-1).reshape(-1, 2)\n\n    # Transform all points at once using the helper function\n    transformed_points = apply_affine_to_points(points, matrix)\n\n    transformed_points = transformed_points.reshape(len(bboxes), -1, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_points[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_points[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_points[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_points[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine_largest_box","title":"def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called \"largest box\" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n>>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n>>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n>>> print(transformed_bboxes)\n[[ 25.  25.  45.  45.   1.]\n [ 65.  65.  85.  85.   2.]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_largest_box(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes and return the largest enclosing boxes.\n\n    This function transforms each corner of every bounding box using the given affine transformation\n    matrix, then computes the new bounding boxes that fully enclose the transformed corners.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The resulting bounding boxes are the smallest axis-aligned boxes that completely\n          enclose the transformed original boxes. They may be larger than the minimal possible\n          bounding box if the original box becomes rotated.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method is called \"largest box\" because it returns the largest axis-aligned box\n          that encloses all corners of the transformed bounding box.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n        >>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n        >>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n        >>> print(transformed_bboxes)\n        [[ 25.  25.  45.  45.   1.]\n         [ 65.  65.  85.  85.   2.]]\n    \"\"\"\n    # Extract corners of all bboxes\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    corners = (\n        np.array([[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]]).transpose(2, 0, 1).reshape(-1, 2)\n    )\n\n    # Transform all corners at once\n    transformed_corners = apply_affine_to_points(corners, matrix).reshape(-1, 4, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_corners[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_corners[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_corners[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_corners[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_d4","title":"def bboxes_d4 (bboxes, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_d4(\n    bboxes: np.ndarray,\n    group_member: D4Type,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a bounding box.\n\n    The function transforms a bounding box according to the specified group member from the `D_4` group.\n    These transformations include rotations and reflections, specified to work on an image's bounding box given\n    its dimensions.\n\n    Parameters:\n    -  bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n    - group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.\n\n    Returns:\n    - BoxInternalType: The transformed bounding box.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Applying a 90-degree rotation:\n      `bbox_d4((10, 20, 110, 120), 'r90')`\n      This would rotate the bounding box 90 degrees within a 100x100 image.\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: bboxes_rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: bboxes_rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: bboxes_rot90(x, 3),  # Rotate 270 degrees\n        \"v\": lambda x: bboxes_vflip(x),  # Vertical flip\n        \"hvt\": lambda x: bboxes_transpose(\n            bboxes_rot90(x, 2),\n        ),  # Reflect over anti-diagonal\n        \"h\": lambda x: bboxes_hflip(x),  # Horizontal flip\n        \"t\": lambda x: bboxes_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](bboxes)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_grid_shuffle","title":"def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n>>> tiles = np.array([\n...     [0, 0, 50, 50],    # top-left tile\n...     [0, 50, 50, 100],  # top-right tile\n...     [50, 0, 100, 50],  # bottom-left tile\n...     [50, 50, 100, 100] # bottom-right tile\n... ])\n>>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n>>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n>>> # Result may contain multiple boxes if the original box was split\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_grid_shuffle(\n    bboxes: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int],\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Apply grid shuffle transformation to bounding boxes.\n\n    This function transforms bounding boxes according to a grid shuffle operation. It handles cases\n    where bounding boxes may be split into multiple components after shuffling and applies\n    filtering based on minimum area and visibility requirements.\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4+) where N is the number of boxes.\n               Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents\n               optional additional fields (e.g., class_id, score).\n        tiles: Array of tile coordinates with shape (M, 4) where M is the number of tiles.\n               Each tile is in format [start_y, start_x, end_y, end_x].\n        mapping: List of indices defining how tiles should be rearranged. Each index i in the list\n                contains the index of the tile that should be moved to position i.\n        image_shape: Shape of the image as (height, width).\n        min_area: Minimum area threshold in pixels. If a component's area after shuffling is\n                 smaller than this value, it will be filtered out. If None, no area filtering\n                 is applied.\n        min_visibility: Minimum visibility ratio threshold in range [0, 1]. Calculated as\n                       (component_area / original_area). If a component's visibility is lower\n                       than this value, it will be filtered out. If None, no visibility\n                       filtering is applied.\n\n    Returns:\n        np.ndarray: Array of transformed bounding boxes with shape (K, 4+) where K is the\n                   number of valid components after shuffling and filtering. The format of\n                   each box matches the input format, preserving any additional fields.\n                   If no valid components remain after filtering, returns an empty array\n                   with shape (0, C) where C matches the input column count.\n\n    Note:\n        - The function converts bboxes to masks before applying the transformation to handle\n          cases where boxes may be split into multiple components.\n        - After shuffling, each component is validated against min_area and min_visibility\n          requirements independently.\n        - Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and\n          copied to all components derived from the same original bbox.\n        - Empty input arrays are handled gracefully and return empty arrays of the\n          appropriate shape.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n        >>> tiles = np.array([\n        ...     [0, 0, 50, 50],    # top-left tile\n        ...     [0, 50, 50, 100],  # top-right tile\n        ...     [50, 0, 100, 50],  # bottom-left tile\n        ...     [50, 50, 100, 100] # bottom-right tile\n        ... ])\n        >>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n        >>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n        >>> # Result may contain multiple boxes if the original box was split\n    \"\"\"\n    # Convert bboxes to masks\n    masks = masks_from_bboxes(bboxes, image_shape)\n\n    # Apply grid shuffle to each mask and handle split components\n    all_component_masks = []\n    extra_bbox_data = []  # Store additional bbox data for each component\n\n    for idx, mask in enumerate(masks):\n        original_area = np.sum(mask)  # Get original mask area\n\n        # Shuffle the mask\n        shuffled_mask = swap_tiles_on_image(mask, tiles, mapping)\n\n        # Find connected components\n        num_components, components = cv2.connectedComponents(\n            shuffled_mask.astype(np.uint8),\n        )\n\n        # For each component, create a separate binary mask\n        for comp_idx in range(1, num_components):  # Skip background (0)\n            component_mask = (components == comp_idx).astype(np.uint8)\n\n            # Calculate area and visibility ratio\n            component_area = np.sum(component_mask)\n            # Check if component meets minimum requirements\n            if is_valid_component(\n                component_area,\n                original_area,\n                min_area,\n                min_visibility,\n            ):\n                all_component_masks.append(component_mask)\n                # Append additional bbox data for this component\n                if bboxes.shape[1] > NUM_BBOXES_COLUMNS_IN_ALBUMENTATIONS:\n                    extra_bbox_data.append(bboxes[idx, 4:])\n\n    # Convert all component masks to bboxes\n    if all_component_masks:\n        all_component_masks = np.array(all_component_masks)\n        shuffled_bboxes = bboxes_from_masks(all_component_masks)\n\n        # Add back additional bbox data if present\n        if extra_bbox_data:\n            extra_bbox_data = np.array(extra_bbox_data)\n            return np.column_stack([shuffled_bboxes, extra_bbox_data])\n    else:\n        # Handle case where no valid components were found\n        return np.zeros((0, bboxes.shape[1]), dtype=bboxes.dtype)\n\n    return shuffled_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_hflip","title":"def bboxes_hflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_hflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally around the y-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of horizontally flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 0] = 1 - bboxes[:, 2]  # new x_min = 1 - x_max\n    flipped_bboxes[:, 2] = 1 - bboxes[:, 0]  # new x_max = 1 - x_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_rot90","title":"def bboxes_rot90 (bboxes, factor) [view source on GitHub]","text":"

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor int

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Exceptions:

Type Description ValueError

If factor is not in set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_rot90(bboxes: np.ndarray, factor: int) -> np.ndarray:\n    \"\"\"Rotates bounding boxes by 90 degrees CCW (see np.rot90)\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n        factor: Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.\n\n    Returns:\n        np.ndarray: A numpy array of rotated bounding boxes with the same shape as input.\n\n    Raises:\n        ValueError: If factor is not in set {0, 1, 2, 3}.\n    \"\"\"\n    if factor not in {0, 1, 2, 3}:\n        raise ValueError(\"Parameter factor must be in set {0, 1, 2, 3}\")\n\n    if factor == 0:\n        return bboxes\n\n    rotated_bboxes = bboxes.copy()\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    if factor == 1:\n        rotated_bboxes[:, 0] = y_min\n        rotated_bboxes[:, 1] = 1 - x_max\n        rotated_bboxes[:, 2] = y_max\n        rotated_bboxes[:, 3] = 1 - x_min\n    elif factor == ROT90_180_FACTOR:\n        rotated_bboxes[:, 0] = 1 - x_max\n        rotated_bboxes[:, 1] = 1 - y_max\n        rotated_bboxes[:, 2] = 1 - x_min\n        rotated_bboxes[:, 3] = 1 - y_min\n    elif factor == ROT90_270_FACTOR:\n        rotated_bboxes[:, 0] = 1 - y_max\n        rotated_bboxes[:, 1] = x_min\n        rotated_bboxes[:, 2] = 1 - y_min\n        rotated_bboxes[:, 3] = x_max\n\n    return rotated_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_transpose","title":"def bboxes_transpose (bboxes) [view source on GitHub]","text":"

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_transpose(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Transpose bounding boxes by swapping x and y coordinates.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of transposed bounding boxes with the same shape as input.\n    \"\"\"\n    transposed_bboxes = bboxes.copy()\n    transposed_bboxes[:, [0, 1, 2, 3]] = bboxes[:, [1, 0, 3, 2]]\n\n    return transposed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_vflip","title":"def bboxes_vflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_vflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes vertically around the x-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of vertically flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 1] = 1 - bboxes[:, 3]  # new y_min = 1 - y_max\n    flipped_bboxes[:, 3] = 1 - bboxes[:, 1]  # new y_max = 1 - y_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.calculate_affine_transform_padding","title":"def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]","text":"

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py Python
def calculate_affine_transform_padding(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate the necessary padding for an affine transformation to avoid empty spaces.\"\"\"\n    height, width = image_shape[:2]\n\n    # Check for identity transform\n    if is_identity_matrix(matrix):\n        return (0, 0, 0, 0)\n\n    # Original corners\n    corners = np.array([[0, 0], [width, 0], [width, height], [0, height]])\n\n    # Transform corners\n    transformed_corners = apply_affine_to_points(corners, matrix)\n\n    # Ensure transformed_corners is 2D\n    transformed_corners = transformed_corners.reshape(-1, 2)\n\n    # Find box that includes both original and transformed corners\n    all_corners = np.vstack((corners, transformed_corners))\n    min_x, min_y = all_corners.min(axis=0)\n    max_x, max_y = all_corners.max(axis=0)\n\n    # Compute the inverse transform\n    inverse_matrix = np.linalg.inv(matrix)\n\n    # Apply inverse transform to all corners of the bounding box\n    bbox_corners = np.array(\n        [[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]],\n    )\n    inverse_corners = apply_affine_to_points(bbox_corners, inverse_matrix).reshape(\n        -1,\n        2,\n    )\n\n    min_x, min_y = inverse_corners.min(axis=0)\n    max_x, max_y = inverse_corners.max(axis=0)\n\n    pad_left = max(0, math.ceil(0 - min_x))\n    pad_right = max(0, math.ceil(max_x - width))\n    pad_top = max(0, math.ceil(0 - min_y))\n    pad_bottom = max(0, math.ceil(max_y - height))\n\n    return pad_left, pad_right, pad_top, pad_bottom\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.center","title":"def center (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates if image. Used by images, masks and keypoints.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2 - 0.5, height / 2 - 0.5\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.center_bbox","title":"def center_bbox (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates for of image for bounding boxes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2, height / 2\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.compute_tps_weights","title":"def compute_tps_weights (src_points, dst_points) [view source on GitHub]","text":"

Compute Thin Plate Spline weights.

Parameters:

Name Type Description src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_tps_weights(\n    src_points: np.ndarray,\n    dst_points: np.ndarray,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute Thin Plate Spline weights.\n\n    Args:\n        src_points: Source control points with shape (num_points, 2)\n        dst_points: Destination control points with shape (num_points, 2)\n\n    Returns:\n        tuple of:\n        - nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)\n        - affine_weights: Weights for affine transformation (3, 2)\n            [constant term, x scale/shear, y scale/shear]\n\n    Note:\n        The TPS interpolation is decomposed into:\n        1. Nonlinear part (controlled by kernel weights)\n        2. Affine part (global scaling, rotation, translation)\n    \"\"\"\n    num_points = src_points.shape[0]\n\n    # Compute pairwise distances\n    distances = np.linalg.norm(src_points[:, None] - src_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    # Add small epsilon to avoid log(0)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Construct affine terms matrix [1, x, y]\n    affine_terms = np.ones((num_points, 3))\n    affine_terms[:, 1:] = src_points\n\n    # Build system matrix\n    system_matrix = np.zeros((num_points + 3, num_points + 3))\n    system_matrix[:num_points, :num_points] = kernel_matrix\n    system_matrix[:num_points, num_points:] = affine_terms\n    system_matrix[num_points:, :num_points] = affine_terms.T\n\n    # Right-hand side of the system\n    target_coords = np.zeros((num_points + 3, 2))\n    target_coords[:num_points] = dst_points\n\n    # Solve the system for both x and y coordinates\n    all_weights = np.linalg.solve(system_matrix, target_coords)\n\n    # Split weights into nonlinear and affine components\n    nonlinear_weights = all_weights[:num_points]\n    affine_weights = all_weights[num_points:]\n\n    return nonlinear_weights, affine_weights\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.compute_transformed_image_bounds","title":"def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]","text":"

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_transformed_image_bounds(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute the bounds of an image after applying an affine transformation.\n\n    Args:\n        matrix (np.ndarray): The 3x3 affine transformation matrix.\n        image_shape (Tuple[int, int]): The shape of the image as (height, width).\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing:\n            - min_coords: An array with the minimum x and y coordinates.\n            - max_coords: An array with the maximum x and y coordinates.\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Define the corners of the image\n    corners = np.array([[0, 0, 1], [width, 0, 1], [width, height, 1], [0, height, 1]])\n\n    # Transform the corners\n    transformed_corners = corners @ matrix.T\n    transformed_corners = transformed_corners[:, :2] / transformed_corners[:, 2:]\n\n    # Calculate the bounding box of the transformed corners\n    min_coords = np.floor(transformed_corners.min(axis=0)).astype(int)\n    max_coords = np.ceil(transformed_corners.max(axis=0)).astype(int)\n\n    return min_coords, max_coords\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_affine_transformation_matrix","title":"def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]","text":"

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_affine_transformation_matrix(\n    translate: XYInt,\n    shear: XYFloat,\n    scale: XYFloat,\n    rotate: float,\n    shift: tuple[float, float],\n) -> np.ndarray:\n    \"\"\"Create an affine transformation matrix combining translation, shear, scale, and rotation.\n\n    Args:\n        translate (dict[str, float]): Translation in x and y directions.\n        shear (dict[str, float]): Shear in x and y directions (in degrees).\n        scale (dict[str, float]): Scale factors for x and y directions.\n        rotate (float): Rotation angle in degrees.\n        shift (tuple[float, float]): Shift to apply before and after transformations.\n\n    Returns:\n        np.ndarray: The resulting 3x3 affine transformation matrix.\n    \"\"\"\n    # Convert angles to radians\n    rotate_rad = np.deg2rad(rotate % 360)\n\n    shear_x_rad = np.deg2rad(shear[\"x\"])\n    shear_y_rad = np.deg2rad(shear[\"y\"])\n\n    # Create individual transformation matrices\n    # 1. Shift to top-left\n    m_shift_topleft = np.array([[1, 0, -shift[0]], [0, 1, -shift[1]], [0, 0, 1]])\n\n    # 2. Scale\n    m_scale = np.array([[scale[\"x\"], 0, 0], [0, scale[\"y\"], 0], [0, 0, 1]])\n\n    # 3. Rotation\n    m_rotate = np.array(\n        [\n            [np.cos(rotate_rad), np.sin(rotate_rad), 0],\n            [-np.sin(rotate_rad), np.cos(rotate_rad), 0],\n            [0, 0, 1],\n        ],\n    )\n\n    # 4. Shear\n    m_shear = np.array(\n        [[1, np.tan(shear_x_rad), 0], [np.tan(shear_y_rad), 1, 0], [0, 0, 1]],\n    )\n\n    # 5. Translation\n    m_translate = np.array([[1, 0, translate[\"x\"]], [0, 1, translate[\"y\"]], [0, 0, 1]])\n\n    # 6. Shift back to center\n    m_shift_center = np.array([[1, 0, shift[0]], [0, 1, shift[1]], [0, 0, 1]])\n\n    # Combine all transformations\n    # The order is important: transformations are applied from right to left\n    m = m_shift_center @ m_translate @ m_shear @ m_rotate @ m_scale @ m_shift_topleft\n\n    # Ensure the last row is exactly [0, 0, 1]\n    m[2] = [0, 0, 1]\n\n    return m\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_piecewise_affine_maps","title":"def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]","text":"

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_piecewise_affine_maps(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    scale: float,\n    absolute_scale: bool,\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray | None, np.ndarray | None]:\n    \"\"\"Create maps for piecewise affine transformation using OpenCV's remap function.\"\"\"\n    height, width = image_shape[:2]\n    nb_rows, nb_cols = grid\n\n    # Input validation\n    if height <= 0 or width <= 0 or nb_rows <= 0 or nb_cols <= 0:\n        raise ValueError(\"Dimensions must be positive\")\n    if scale <= 0:\n        return None, None\n\n    # Create source points grid\n    y = np.linspace(0, height - 1, nb_rows, dtype=np.float32)\n    x = np.linspace(0, width - 1, nb_cols, dtype=np.float32)\n    xx_src, yy_src = np.meshgrid(x, y)\n\n    # Initialize destination maps at full resolution\n    map_x = np.zeros((height, width), dtype=np.float32)\n    map_y = np.zeros((height, width), dtype=np.float32)\n\n    # Generate jitter for control points\n    jitter_scale = scale / 3 if absolute_scale else scale * min(width, height) / 3\n\n    jitter = random_generator.normal(0, jitter_scale, (nb_rows, nb_cols, 2)).astype(\n        np.float32,\n    )\n\n    # Create control points with jitter\n    control_points = np.zeros((nb_rows * nb_cols, 4), dtype=np.float32)\n    for i in range(nb_rows):\n        for j in range(nb_cols):\n            idx = i * nb_cols + j\n            # Source points\n            control_points[idx, 0] = xx_src[i, j]\n            control_points[idx, 1] = yy_src[i, j]\n            # Destination points with jitter\n            control_points[idx, 2] = np.clip(\n                xx_src[i, j] + jitter[i, j, 1],\n                0,\n                width - 1,\n            )\n            control_points[idx, 3] = np.clip(\n                yy_src[i, j] + jitter[i, j, 0],\n                0,\n                height - 1,\n            )\n\n    # Create full resolution maps\n    for i in range(height):\n        for j in range(width):\n            # Find nearest control points and interpolate\n            dx = j - control_points[:, 0]\n            dy = i - control_points[:, 1]\n            dist = dx * dx + dy * dy\n            weights = 1 / (dist + 1e-8)\n            weights = weights / np.sum(weights)\n\n            map_x[i, j] = np.sum(weights * control_points[:, 2])\n            map_y[i, j] = np.sum(weights * control_points[:, 3])\n\n    # Ensure output is within bounds\n    map_x = np.clip(map_x, 0, width - 1, out=map_x)\n    map_y = np.clip(map_y, 0, height - 1, out=map_y)\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_shape_groups","title":"def create_shape_groups (tiles) [view source on GitHub]","text":"

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:\n    \"\"\"Groups tiles by their shape and stores the indices for each shape.\"\"\"\n    shape_groups = defaultdict(list)\n    for index, (start_y, start_x, end_y, end_x) in enumerate(tiles):\n        shape = (end_y - start_y, end_x - start_x)\n        shape_groups[shape].append(index)\n    return shape_groups\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.d4","title":"def d4 (img, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to an image array.\n\n    This function manipulates an image using transformations such as rotations and flips,\n    corresponding to the `D_4` dihedral group symmetry operations.\n    Each transformation is identified by a unique group member code.\n\n    Parameters:\n    - img (np.ndarray): The input image array to transform.\n    - group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:\n      - 'e': Identity (no transformation).\n      - 'r90': Rotate 90 degrees counterclockwise.\n      - 'r180': Rotate 180 degrees.\n      - 'r270': Rotate 270 degrees counterclockwise.\n      - 'v': Vertical flip.\n      - 'hvt': Transpose over second diagonal\n      - 'h': Horizontal flip.\n      - 't': Transpose (reflect over the main diagonal).\n\n    Returns:\n    - np.ndarray: The transformed image array.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Rotating an image by 90 degrees:\n      `transformed_image = d4(original_image, 'r90')`\n    - Applying a horizontal flip to an image:\n      `transformed_image = d4(original_image, 'h')`\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: rot90(x, 3),  # Rotate 270 degrees\n        \"v\": vflip,  # Vertical flip\n        \"hvt\": lambda x: transpose(rot90(x, 2)),  # Reflect over anti-diagonal\n        \"h\": hflip,  # Horizontal flip\n        \"t\": transpose,  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](img)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.distort_image","title":"def distort_image (image, generated_mesh, interpolation) [view source on GitHub]","text":"

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n>>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n>>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n>>> distorted.shape\n(100, 100, 3)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@preserve_channel_dim\ndef distort_image(\n    image: np.ndarray,\n    generated_mesh: np.ndarray,\n    interpolation: int,\n) -> np.ndarray:\n    \"\"\"Apply perspective distortion to an image based on a generated mesh.\n\n    This function applies a perspective transformation to each cell of the image defined by the\n    generated mesh. The distortion is applied using OpenCV's perspective transformation and\n    blending techniques.\n\n    Args:\n        image (np.ndarray): The input image to be distorted. Can be a 2D grayscale image or a\n                            3D color image.\n        generated_mesh (np.ndarray): A 2D array where each row represents a quadrilateral cell\n                                    as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4].\n                                    The first four values define the source rectangle, and the last eight values\n                                    define the destination quadrilateral.\n        interpolation (int): Interpolation method to be used in the perspective transformation.\n                             Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).\n\n    Returns:\n        np.ndarray: The distorted image with the same shape and dtype as the input image.\n\n    Note:\n        - The function preserves the channel dimension of the input image.\n        - Each cell of the generated mesh is transformed independently and then blended into the output image.\n        - The distortion is applied using perspective transformation, which allows for more complex\n          distortions compared to affine transformations.\n\n    Example:\n        >>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n        >>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n        >>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n        >>> distorted.shape\n        (100, 100, 3)\n    \"\"\"\n    distorted_image = np.zeros_like(image)\n\n    for mesh in generated_mesh:\n        # Extract source rectangle and destination quadrilateral\n        x1, y1, x2, y2 = mesh[:4]  # Source rectangle\n        dst_quad = mesh[4:].reshape(4, 2)  # Destination quadrilateral\n\n        # Convert source rectangle to quadrilateral\n        src_quad = np.array(\n            [\n                [x1, y1],  # Top-left\n                [x2, y1],  # Top-right\n                [x2, y2],  # Bottom-right\n                [x1, y2],  # Bottom-left\n            ],\n            dtype=np.float32,\n        )\n\n        # Calculate Perspective transformation matrix\n        perspective_mat = cv2.getPerspectiveTransform(src_quad, dst_quad)\n\n        # Apply Perspective transformation\n        warped = cv2.warpPerspective(\n            image,\n            perspective_mat,\n            (image.shape[1], image.shape[0]),\n            flags=interpolation,\n        )\n\n        # Create mask for the transformed region\n        mask = np.zeros(image.shape[:2], dtype=np.uint8)\n        cv2.fillConvexPoly(mask, np.int32(dst_quad), 255)\n\n        # Copy only the warped quadrilateral area to the output image\n        distorted_image = cv2.copyTo(warped, mask, distorted_image)\n\n    return distorted_image\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.find_keypoint","title":"def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]","text":"

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py Python
def find_keypoint(\n    position: tuple[int, int],\n    distance_map: np.ndarray,\n    threshold: float | None,\n    inverted: bool,\n) -> tuple[float, float] | None:\n    \"\"\"Determine if a valid keypoint can be found at the given position.\"\"\"\n    y, x = position\n    value = distance_map[y, x]\n    if not inverted and threshold is not None and value >= threshold:\n        return None\n    if inverted and threshold is not None and value <= threshold:\n        return None\n    return float(x), float(y)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.flip_bboxes","title":"def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]","text":"

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef flip_bboxes(\n    bboxes: np.ndarray,\n    flip_horizontal: bool = False,\n    flip_vertical: bool = False,\n    image_shape: tuple[int, int] = (0, 0),\n) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally and/or vertically.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where each row is\n            [x_min, y_min, x_max, y_max, ...].\n        flip_horizontal (bool): Whether to flip horizontally.\n        flip_vertical (bool): Whether to flip vertically.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Flipped bounding boxes.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    flipped_bboxes = bboxes.copy()\n    if flip_horizontal:\n        flipped_bboxes[:, [0, 2]] = cols - flipped_bboxes[:, [2, 0]]\n    if flip_vertical:\n        flipped_bboxes[:, [1, 3]] = rows - flipped_bboxes[:, [3, 1]]\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.from_distance_maps","title":"def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]","text":"

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n>>> inverted = True\n>>> if_not_found_coords = [0, 0]\n>>> threshold = 0.5\n>>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n>>> print(keypoints.shape)\n(3, 2)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def from_distance_maps(\n    distance_maps: np.ndarray,\n    inverted: bool,\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None = None,\n    threshold: float | None = None,\n) -> np.ndarray:\n    \"\"\"Convert distance maps back to keypoints coordinates.\n\n    This function is the inverse of `to_distance_maps`. It takes distance maps generated for a set of keypoints\n    and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps,\n    and can handle cases where keypoints are not found or fall outside a specified threshold.\n\n    Args:\n        distance_maps (np.ndarray): A 3D numpy array of shape (height, width, nb_keypoints) containing\n            distance maps for each keypoint. Each channel represents the distance map for one keypoint.\n        inverted (bool): If True, treats the distance maps as inverted (where higher values indicate\n            closer proximity to keypoints). If False, treats them as regular distance maps (where lower\n            values indicate closer proximity).\n        if_not_found_coords (Sequence[int] | dict[str, Any] | None, optional): Coordinates to use for\n            keypoints that are not found or fall outside the threshold. Can be:\n            - None: Drop keypoints that are not found.\n            - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints.\n            - Dict with 'x' and 'y' keys: Use these values for not found keypoints.\n            Defaults to None.\n        threshold (float | None, optional): A threshold value to determine valid keypoints. For inverted\n            maps, values >= threshold are considered valid. For regular maps, values <= threshold are\n            considered valid. If None, all keypoints are considered valid. Defaults to None.\n\n    Returns:\n        np.ndarray: A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates\n        of the reconstructed keypoints. If `drop_if_not_found` is True (derived from if_not_found_coords),\n        the output may have fewer rows than input keypoints.\n\n    Raises:\n        ValueError: If the input `distance_maps` is not a 3D array.\n\n    Notes:\n        - The function uses vectorized operations for improved performance, especially with large numbers of keypoints.\n        - When `threshold` is None, all keypoints are considered valid, and `if_not_found_coords` is not used.\n        - The function assumes that the input distance maps are properly normalized and scaled according to the\n          original image dimensions.\n\n    Example:\n        >>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n        >>> inverted = True\n        >>> if_not_found_coords = [0, 0]\n        >>> threshold = 0.5\n        >>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n        >>> print(keypoints.shape)\n        (3, 2)\n    \"\"\"\n    if distance_maps.ndim != NUM_MULTI_CHANNEL_DIMENSIONS:\n        msg = f\"Expected three-dimensional input, got {distance_maps.ndim} dimensions and shape {distance_maps.shape}.\"\n        raise ValueError(msg)\n    height, width, nb_keypoints = distance_maps.shape\n\n    drop_if_not_found, if_not_found_x, if_not_found_y = validate_if_not_found_coords(\n        if_not_found_coords,\n    )\n\n    # Find the indices of max/min values for all keypoints at once\n    if inverted:\n        hitidx_flat = np.argmax(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n    else:\n        hitidx_flat = np.argmin(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n\n    # Convert flat indices to 2D coordinates\n    hitidx_y, hitidx_x = np.unravel_index(hitidx_flat, (height, width))\n\n    # Create keypoints array\n    keypoints = np.column_stack((hitidx_x, hitidx_y)).astype(float)\n\n    if threshold is not None:\n        # Check threshold condition\n        if inverted:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] >= threshold\n        else:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] <= threshold\n\n        if not drop_if_not_found:\n            # Replace invalid keypoints with if_not_found_coords\n            keypoints[~valid_mask] = [if_not_found_x, if_not_found_y]\n        else:\n            # Keep only valid keypoints\n            return keypoints[valid_mask]\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_displacement_fields","title":"def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]","text":"

Generate displacement fields for elastic transform.

Parameters:

Name Type Description image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use (\"gaussian\" or \"uniform\")

Returns:

Type Description tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_displacement_fields(\n    image_shape: tuple[int, int],\n    alpha: float,\n    sigma: float,\n    same_dxdy: bool,\n    kernel_size: tuple[int, int],\n    random_generator: np.random.Generator,\n    noise_distribution: Literal[\"gaussian\", \"uniform\"],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate displacement fields for elastic transform.\n\n    Args:\n        image_shape: Shape of the image (height, width)\n        alpha: Scaling factor for displacement\n        sigma: Standard deviation for Gaussian blur\n        same_dxdy: Whether to use same displacement field for both directions\n        kernel_size: Size of Gaussian blur kernel\n        random_generator: NumPy random number generator\n        noise_distribution: Type of noise distribution to use (\"gaussian\" or \"uniform\")\n\n    Returns:\n        tuple: (dx, dy) displacement fields\n    \"\"\"\n\n    def generate_noise_field() -> np.ndarray:\n        # Generate noise based on distribution type\n        if noise_distribution == \"gaussian\":\n            field = random_generator.standard_normal(size=image_shape[:2])\n        else:  # uniform\n            field = random_generator.uniform(low=-1, high=1, size=image_shape[:2])\n\n        # Common operations for both distributions\n        field = field.astype(np.float32)\n        cv2.GaussianBlur(field, kernel_size, sigma, dst=field)\n        return field * alpha\n\n    # Generate first displacement field\n    dx = generate_noise_field()\n\n    # Generate or copy second displacement field\n    dy = dx if same_dxdy else generate_noise_field()\n\n    return dx, dy\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_distorted_grid_polygons","title":"def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]","text":"

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n>>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n>>> distorted.shape\n(4, 8)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_distorted_grid_polygons(\n    dimensions: np.ndarray,\n    magnitude: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate distorted grid polygons based on input dimensions and magnitude.\n\n    This function creates a grid of polygons and applies random distortions to the internal vertices,\n    while keeping the boundary vertices fixed. The distortion is applied consistently across shared\n    vertices to avoid gaps or overlaps in the resulting grid.\n\n    Args:\n        dimensions (np.ndarray): A 3D array of shape (grid_height, grid_width, 4) where each element\n                                 is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.\n        magnitude (int): Maximum pixel-wise displacement for distortion. The actual displacement\n                         will be randomly chosen in the range [-magnitude, magnitude].\n        random_generator (np.random.Generator): A random number generator.\n\n    Returns:\n        np.ndarray: A 2D array of shape (total_cells, 8) where each row represents a distorted polygon\n                    as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.\n\n    Note:\n        - Only internal grid points are distorted; boundary points remain fixed.\n        - The function ensures consistent distortion across shared vertices of adjacent cells.\n        - The distortion is applied to the following points of each internal cell:\n            * Bottom-right of the cell above and to the left\n            * Bottom-left of the cell above\n            * Top-right of the cell to the left\n            * Top-left of the current cell\n        - Each square represents a cell, and the X marks indicate the coordinates where displacement occurs.\n            +--+--+--+--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--+--+--+--+\n        - For each X, the coordinates of the left, right, top, and bottom edges\n          in the four adjacent cells are displaced.\n\n    Example:\n        >>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n        ...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n        >>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n        >>> distorted.shape\n        (4, 8)\n    \"\"\"\n    grid_height, grid_width = dimensions.shape[:2]\n    total_cells = grid_height * grid_width\n\n    # Initialize polygons\n    polygons = np.zeros((total_cells, 8), dtype=np.float32)\n    polygons[:, 0:2] = dimensions.reshape(-1, 4)[:, [0, 1]]  # x1, y1\n    polygons[:, 2:4] = dimensions.reshape(-1, 4)[:, [2, 1]]  # x2, y1\n    polygons[:, 4:6] = dimensions.reshape(-1, 4)[:, [2, 3]]  # x2, y2\n    polygons[:, 6:8] = dimensions.reshape(-1, 4)[:, [0, 3]]  # x1, y2\n\n    # Generate displacements for internal grid points only\n    internal_points_height, internal_points_width = grid_height - 1, grid_width - 1\n    displacements = random_generator.integers(\n        -magnitude,\n        magnitude + 1,\n        size=(internal_points_height, internal_points_width, 2),\n    ).astype(np.float32)\n\n    # Apply displacements to internal polygon vertices\n    for i in range(1, grid_height):\n        for j in range(1, grid_width):\n            dx, dy = displacements[i - 1, j - 1]\n\n            # Bottom-right of cell (i-1, j-1)\n            polygons[(i - 1) * grid_width + (j - 1), 4:6] += [dx, dy]\n\n            # Bottom-left of cell (i-1, j)\n            polygons[(i - 1) * grid_width + j, 6:8] += [dx, dy]\n\n            # Top-right of cell (i, j-1)\n            polygons[i * grid_width + (j - 1), 2:4] += [dx, dy]\n\n            # Top-left of cell (i, j)\n            polygons[i * grid_width + j, 0:2] += [dx, dy]\n\n    return polygons\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_grid","title":"def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]","text":"

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)\n>>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n>>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n>>> num_steps = 5\n>>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n>>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_grid(\n    image_shape: tuple[int, int],\n    steps_x: list[float],\n    steps_y: list[float],\n    num_steps: int,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate a distorted grid for image transformation based on given step sizes.\n\n    This function creates two 2D arrays (map_x and map_y) that represent a distorted version\n    of the original image grid. These arrays can be used with OpenCV's remap function to\n    apply grid distortion to an image.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        steps_x (list[float]): List of step sizes for the x-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the x direction.\n        steps_y (list[float]): List of step sizes for the y-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the y direction.\n        num_steps (int): The number of steps to divide each axis into. This determines\n            the granularity of the distortion grid.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing two 2D numpy arrays:\n            - map_x: A 2D array of float32 values representing the x-coordinates\n              of the distorted grid.\n            - map_y: A 2D array of float32 values representing the y-coordinates\n              of the distorted grid.\n\n    Note:\n        - The function generates a grid where each cell can be distorted independently.\n        - The distortion is controlled by the steps_x and steps_y parameters, which\n          determine how much each grid line is shifted.\n        - The resulting map_x and map_y can be used directly with cv2.remap() to\n          apply the distortion to an image.\n        - The distortion is applied smoothly across each grid cell using linear\n          interpolation.\n\n    Example:\n        >>> image_shape = (100, 100)\n        >>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n        >>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n        >>> num_steps = 5\n        >>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n        >>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n    \"\"\"\n    height, width = image_shape[:2]\n    x_step = width // num_steps\n    xx = np.zeros(width, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_x):\n        x = idx * x_step\n        start = int(x)\n        end = min(int(x) + x_step, width)\n        cur = prev + x_step * step\n        xx[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    y_step = height // num_steps\n    yy = np.zeros(height, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_y):\n        y = idx * y_step\n        start = int(y)\n        end = min(int(y) + y_step, height)\n        cur = prev + y_step * step\n        yy[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    return np.meshgrid(xx, yy)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_reflected_bboxes","title":"def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_bboxes(\n    bboxes: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected bounding boxes for the entire reflection grid.\n\n    Args:\n        bboxes (np.ndarray): Original bounding boxes.\n        grid_dims (dict[str, tuple[int, int]]): Grid dimensions and original position.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted bounding boxes for the entire grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of bboxes\n    bboxes_hflipped = flip_bboxes(bboxes, flip_horizontal=True, image_shape=image_shape)\n    bboxes_vflipped = flip_bboxes(bboxes, flip_vertical=True, image_shape=image_shape)\n    bboxes_hvflipped = flip_bboxes(\n        bboxes,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [\n            original_col * cols,\n            original_row * rows,\n            original_col * cols,\n            original_row * rows,\n        ],\n    )\n    bboxes = shift_bboxes(bboxes, shift_vector)\n    bboxes_hflipped = shift_bboxes(bboxes_hflipped, shift_vector)\n    bboxes_vflipped = shift_bboxes(bboxes_vflipped, shift_vector)\n    bboxes_hvflipped = shift_bboxes(bboxes_hvflipped, shift_vector)\n\n    new_bboxes = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of bboxes to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes\n            elif (grid_row - original_row) % 2 == 0:\n                current_bboxes = bboxes_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes_vflipped\n            else:\n                current_bboxes = bboxes_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                ],\n            )\n            shifted_bboxes = shift_bboxes(current_bboxes, cell_shift)\n\n            new_bboxes.append(shifted_bboxes)\n\n    result = np.vstack(new_bboxes)\n\n    return shift_bboxes(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_reflected_keypoints","title":"def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols) - \"original_position\": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_keypoints(\n    keypoints: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected keypoints for the entire reflection grid.\n\n    This function creates a grid of keypoints by reflecting and shifting the original keypoints.\n    It handles both centered and non-centered grids based on the `center_in_origin` parameter.\n\n    Args:\n        keypoints (np.ndarray): Original keypoints array of shape (N, 4+), where N is the number of keypoints,\n                                and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).\n        grid_dims (dict[str, tuple[int, int]]): A dictionary containing grid dimensions and original position.\n            It should have the following keys:\n            - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols)\n            - \"original_position\": tuple[int, int] representing (original_row, original_col)\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool, optional): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted keypoints for the entire grid. The shape is\n                    (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.\n\n    Note:\n        - The function handles keypoint flipping and shifting to create a grid of reflected keypoints.\n        - It preserves the angle and scale information of the keypoints during transformations.\n        - The resulting grid can be either centered at the origin or positioned based on the original grid.\n    \"\"\"\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of keypoints\n    keypoints_hflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        image_shape=image_shape,\n    )\n    keypoints_vflipped = flip_keypoints(\n        keypoints,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n    keypoints_hvflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    rows, cols = image_shape[:2]\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [original_col * cols, original_row * rows, 0, 0],\n    )  # Only shift x and y\n    keypoints = shift_keypoints(keypoints, shift_vector)\n    keypoints_hflipped = shift_keypoints(keypoints_hflipped, shift_vector)\n    keypoints_vflipped = shift_keypoints(keypoints_vflipped, shift_vector)\n    keypoints_hvflipped = shift_keypoints(keypoints_hvflipped, shift_vector)\n\n    new_keypoints = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of keypoints to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints\n            elif (grid_row - original_row) % 2 == 0:\n                current_keypoints = keypoints_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints_vflipped\n            else:\n                current_keypoints = keypoints_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    0,\n                    0,\n                ],\n            )\n            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)\n\n            new_keypoints.append(shifted_keypoints)\n\n    result = np.vstack(new_keypoints)\n\n    return shift_keypoints(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_shuffled_splits","title":"def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]","text":"

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_shuffled_splits(\n    size: int,\n    divisions: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate shuffled splits for a given dimension size and number of divisions.\n\n    Args:\n        size (int): Total size of the dimension (height or width).\n        divisions (int): Number of divisions (rows or columns).\n        random_generator (np.random.Generator | None): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: Cumulative edges of the shuffled intervals.\n    \"\"\"\n    intervals = almost_equal_intervals(size, divisions)\n    random_generator.shuffle(intervals)\n    return np.insert(np.cumsum(intervals), 0, 0)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_camera_matrix_distortion_maps","title":"def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_camera_matrix_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using camera matrix model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n    camera_matrix = np.array(\n        [[width, 0, center_xy[0]], [0, height, center_xy[1]], [0, 0, 1]],\n        dtype=np.float32,\n    )\n    distortion = np.array([k, k, 0, 0, 0], dtype=np.float32)\n    return cv2.initUndistortRectifyMap(\n        camera_matrix,\n        distortion,\n        None,\n        None,\n        (width, height),\n        cv2.CV_32FC1,\n    )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_dimension_padding","title":"def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]","text":"

Calculate padding for a single dimension.

Parameters:

Name Type Description current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_dimension_padding(\n    current_size: int,\n    min_size: int | None,\n    divisor: int | None,\n) -> tuple[int, int]:\n    \"\"\"Calculate padding for a single dimension.\n\n    Args:\n        current_size: Current size of the dimension\n        min_size: Minimum size requirement, if any\n        divisor: Divisor for padding to make size divisible, if any\n\n    Returns:\n        tuple[int, int]: (pad_before, pad_after)\n    \"\"\"\n    if min_size is not None:\n        if current_size < min_size:\n            pad_before = int((min_size - current_size) / 2.0)\n            pad_after = min_size - current_size - pad_before\n            return pad_before, pad_after\n    elif divisor is not None:\n        remainder = current_size % divisor\n        if remainder > 0:\n            total_pad = divisor - remainder\n            pad_before = total_pad // 2\n            pad_after = total_pad - pad_before\n            return pad_before, pad_after\n\n    return 0, 0\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_fisheye_distortion_maps","title":"def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using fisheye model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_fisheye_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using fisheye model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n\n    center_x, center_y = center_xy\n\n    # Create coordinate grid\n    y, x = np.mgrid[:height, :width].astype(np.float32)\n\n    x = x - center_x\n    y = y - center_y\n\n    # Calculate polar coordinates\n    r = np.sqrt(x * x + y * y)\n    theta = np.arctan2(y, x)\n\n    # Normalize radius by the maximum possible radius to keep distortion in check\n    max_radius = math.sqrt(max(center_x, width - center_x) ** 2 + max(center_y, height - center_y) ** 2)\n    r_norm = r / max_radius\n\n    # Apply fisheye distortion to normalized radius\n    r_dist = r * (1 + k * r_norm * r_norm)\n\n    # Convert back to cartesian coordinates\n    map_x = r_dist * np.cos(theta) + center_x\n    map_y = r_dist * np.sin(theta) + center_y\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_pad_grid_dimensions","title":"def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]","text":"

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def get_pad_grid_dimensions(\n    pad_top: int,\n    pad_bottom: int,\n    pad_left: int,\n    pad_right: int,\n    image_shape: tuple[int, int],\n) -> dict[str, tuple[int, int]]:\n    \"\"\"Calculate the dimensions of the grid needed for reflection padding and the position of the original image.\n\n    Args:\n        pad_top (int): Number of pixels to pad above the image.\n        pad_bottom (int): Number of pixels to pad below the image.\n        pad_left (int): Number of pixels to pad to the left of the image.\n        pad_right (int): Number of pixels to pad to the right of the image.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n\n    Returns:\n        dict[str, tuple[int, int]]: A dictionary containing:\n            - 'grid_shape': A tuple (grid_rows, grid_cols) where:\n                - grid_rows (int): Number of times the image needs to be repeated vertically.\n                - grid_cols (int): Number of times the image needs to be repeated horizontally.\n            - 'original_position': A tuple (original_row, original_col) where:\n                - original_row (int): Row index of the original image in the grid.\n                - original_col (int): Column index of the original image in the grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    grid_rows = 1 + math.ceil(pad_top / rows) + math.ceil(pad_bottom / rows)\n    grid_cols = 1 + math.ceil(pad_left / cols) + math.ceil(pad_right / cols)\n    original_row = math.ceil(pad_top / rows)\n    original_col = math.ceil(pad_left / cols)\n\n    return {\n        \"grid_shape\": (grid_rows, grid_cols),\n        \"original_position\": (original_row, original_col),\n    }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_padding_params","title":"def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]","text":"

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_padding_params(\n    image_shape: tuple[int, int],\n    min_height: int | None,\n    min_width: int | None,\n    pad_height_divisor: int | None,\n    pad_width_divisor: int | None,\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate padding parameters based on target dimensions.\n\n    Args:\n        image_shape: (height, width) of the image\n        min_height: Minimum height requirement, if any\n        min_width: Minimum width requirement, if any\n        pad_height_divisor: Divisor for height padding, if any\n        pad_width_divisor: Divisor for width padding, if any\n\n    Returns:\n        tuple[int, int, int, int]: (pad_top, pad_bottom, pad_left, pad_right)\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    h_pad_top, h_pad_bottom = get_dimension_padding(\n        rows,\n        min_height,\n        pad_height_divisor,\n    )\n    w_pad_left, w_pad_right = get_dimension_padding(cols, min_width, pad_width_divisor)\n\n    return h_pad_top, h_pad_bottom, w_pad_left, w_pad_right\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.is_identity_matrix","title":"def is_identity_matrix (matrix) [view source on GitHub]","text":"

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_identity_matrix(matrix: np.ndarray) -> bool:\n    \"\"\"Check if the given matrix is an identity matrix.\n\n    Args:\n        matrix (np.ndarray): A 3x3 affine transformation matrix.\n\n    Returns:\n        bool: True if the matrix is an identity matrix, False otherwise.\n    \"\"\"\n    return np.allclose(matrix, np.eye(3, dtype=matrix.dtype))\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.is_valid_component","title":"def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]","text":"

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_valid_component(\n    component_area: float,\n    original_area: float,\n    min_area: float | None,\n    min_visibility: float | None,\n) -> bool:\n    \"\"\"Validate if a component meets the minimum requirements.\"\"\"\n    visibility = component_area / original_area\n    return (min_area is None or component_area >= min_area) and (min_visibility is None or visibility >= min_visibility)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_affine","title":"def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]","text":"

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])\n>>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n>>> scale = {'x': 1.5, 'y': 1.2}\n>>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_affine(\n    keypoints: np.ndarray,\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n    scale: XYFloat,\n    border_mode: int,\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to keypoints.\n\n    This function transforms keypoints using the given affine transformation matrix.\n    It handles reflection padding if necessary, updates coordinates, angles, and scales.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where N is the number of keypoints.\n                                Each keypoint is represented as [x, y, angle, scale, ...].\n        matrix (np.ndarray): The 2x3 or 3x3 affine transformation matrix.\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        scale (dict[str, float]): Dictionary containing scale factors for x and y directions.\n                                  Expected keys are 'x' and 'y'.\n        border_mode (int): Border mode for handling keypoints near image edges.\n                            Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.\n\n    Returns:\n        np.ndarray: Transformed keypoints array with the same shape as input.\n\n    Notes:\n        - The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.\n        - Coordinates (x, y) are transformed using the affine matrix.\n        - Angles are adjusted based on the rotation component of the affine transformation.\n        - Scales are multiplied by the maximum of x and y scale factors.\n        - The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.\n\n    Example:\n        >>> keypoints = np.array([[100, 100, 0, 1]])\n        >>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n        >>> scale = {'x': 1.5, 'y': 1.2}\n        >>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    if is_identity_matrix(matrix):\n        return keypoints\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        keypoints = generate_reflected_keypoints(\n            keypoints,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Extract x, y coordinates\n    xy = keypoints[:, :2]\n\n    # Ensure matrix is 2x3\n    if matrix.shape == (3, 3):\n        matrix = matrix[:2]\n\n    # Transform x, y coordinates\n    xy_transformed = cv2.transform(xy.reshape(-1, 1, 2), matrix).squeeze()\n\n    # Calculate angle adjustment\n    angle_adjustment = rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=False)\n\n    # Update angles\n    keypoints[:, 2] = keypoints[:, 2] + angle_adjustment\n\n    # Update scales\n    max_scale = max(scale[\"x\"], scale[\"y\"])\n\n    keypoints[:, 3] *= max_scale\n\n    # Update x, y coordinates\n    keypoints[:, :2] = xy_transformed\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_d4","title":"def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_d4(\n    keypoints: np.ndarray,\n    group_member: D4Type,\n    image_shape: tuple[int, int],\n    **params: Any,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a keypoint.\n\n    This function adjusts a keypoint's coordinates according to the specified `D_4` group transformation,\n    which includes rotations and reflections suitable for image processing tasks. These transformations account\n    for the dimensions of the image to ensure the keypoint remains within its boundaries.\n\n    Parameters:\n    - keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n    -group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.\n    - image_shape (tuple[int, int]): The shape of the image.\n    - params (Any): Not used\n\n    Returns:\n    - KeypointInternalType: The transformed keypoint.\n\n    Raises:\n    - ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.\n\n    Examples:\n    - Rotating a keypoint by 90 degrees in a 100x100 image:\n      `keypoint_d4((50, 30), 'r90', 100, 100)`\n      This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: keypoints_rot90(x, 1, image_shape),  # Rotate 90 degrees\n        \"r180\": lambda x: keypoints_rot90(x, 2, image_shape),  # Rotate 180 degrees\n        \"r270\": lambda x: keypoints_rot90(x, 3, image_shape),  # Rotate 270 degrees\n        \"v\": lambda x: keypoints_vflip(x, rows),  # Vertical flip\n        \"hvt\": lambda x: keypoints_transpose(\n            keypoints_rot90(x, 2, image_shape),\n        ),  # Reflect over anti diagonal\n        \"h\": lambda x: keypoints_hflip(x, cols),  # Horizontal flip\n        \"t\": lambda x: keypoints_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](keypoints)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_hflip","title":"def keypoints_hflip (keypoints, cols) [view source on GitHub]","text":"

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_hflip(keypoints: np.ndarray, cols: int) -> np.ndarray:\n    \"\"\"Flip keypoints horizontally around the y-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        cols: Image width.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip x-coordinates\n    flipped_keypoints[:, 0] = (cols - 1) - keypoints[:, 0]\n\n    # Adjust angles\n    flipped_keypoints[:, 2] = np.pi - keypoints[:, 2]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_rot90","title":"def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]","text":"

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description np.ndarray

The rotated keypoints with the same shape as the input.

Exceptions:

Type Description ValueError

If the factor is not in the set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_rot90(\n    keypoints: np.ndarray,\n    factor: int,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        factor (int): The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n\n    Returns:\n        np.ndarray: The rotated keypoints with the same shape as the input.\n\n    Raises:\n        ValueError: If the factor is not in the set {0, 1, 2, 3}.\n    \"\"\"\n    if factor not in {0, 1, 2, 3}:\n        raise ValueError(\"Parameter factor must be in set {0, 1, 2, 3}\")\n\n    if factor == 0:\n        return keypoints\n\n    height, width = image_shape[:2]\n    rotated_keypoints = keypoints.copy().astype(np.float32)\n\n    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2]\n\n    if factor == 1:\n        rotated_keypoints[:, 0] = y\n        rotated_keypoints[:, 1] = width - 1 - x\n        rotated_keypoints[:, 2] = angle - np.pi / 2\n    elif factor == ROT90_180_FACTOR:\n        rotated_keypoints[:, 0] = width - 1 - x\n        rotated_keypoints[:, 1] = height - 1 - y\n        rotated_keypoints[:, 2] = angle - np.pi\n    elif factor == ROT90_270_FACTOR:\n        rotated_keypoints[:, 0] = height - 1 - y\n        rotated_keypoints[:, 1] = x\n        rotated_keypoints[:, 2] = angle + np.pi / 2\n\n    return rotated_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_scale","title":"def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]","text":"

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description np.ndarray

A numpy array of scaled keypoints with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_scale(\n    keypoints: np.ndarray,\n    scale_x: float,\n    scale_y: float,\n) -> np.ndarray:\n    \"\"\"Scales keypoints by scale_x and scale_y.\n\n    Args:\n        keypoints: A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        scale_x: Scale coefficient x-axis.\n        scale_y: Scale coefficient y-axis.\n\n    Returns:\n        A numpy array of scaled keypoints with the same shape as input.\n    \"\"\"\n    # Extract x, y, angle, and scale\n    x, y, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n    )\n\n    # Scale x and y\n    x_scaled = x * scale_x\n    y_scaled = y * scale_y\n\n    # Scale the keypoint scale by the maximum of scale_x and scale_y\n    scale_scaled = scale * max(scale_x, scale_y)\n\n    # Create the output array\n    scaled_keypoints = np.column_stack([x_scaled, y_scaled, angle, scale_scaled])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],\n        )\n\n    return scaled_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_transpose","title":"def keypoints_transpose (keypoints) [view source on GitHub]","text":"

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_transpose(keypoints: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes keypoints along the main diagonal.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n\n    Returns:\n        np.ndarray: An array of transposed keypoints with the same shape as the input.\n    \"\"\"\n    transposed_keypoints = keypoints.copy()\n\n    # Swap x and y coordinates\n    transposed_keypoints[:, [0, 1]] = keypoints[:, [1, 0]]\n\n    # Adjust angles to reflect the coordinate swap\n    angles = keypoints[:, 2]\n    transposed_keypoints[:, 2] = np.where(\n        angles <= np.pi,\n        np.pi / 2 - angles,\n        3 * np.pi / 2 - angles,\n    )\n\n    return transposed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_vflip","title":"def keypoints_vflip (keypoints, rows) [view source on GitHub]","text":"

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_vflip(keypoints: np.ndarray, rows: int) -> np.ndarray:\n    \"\"\"Flip keypoints vertically around the x-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        rows: Image height.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip y-coordinates\n    flipped_keypoints[:, 1] = (rows - 1) - keypoints[:, 1]\n\n    # Negate angles\n    flipped_keypoints[:, 2] = -keypoints[:, 2]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.perspective_bboxes","title":"def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n>>> image_shape = (100, 100)\n>>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n>>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef perspective_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Applies perspective transformation to bounding boxes.\n\n    This function transforms bounding boxes using the given perspective transformation matrix.\n    It handles bounding boxes with additional attributes beyond the standard coordinates.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (num_bboxes, 4+).\n                             Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n                             Additional columns beyond the first 4 are preserved unchanged.\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        matrix (np.ndarray): The perspective transformation matrix.\n        max_width (int): The maximum width of the output image.\n        max_height (int): The maximum height of the output image.\n        keep_size (bool): If True, maintains the original image size after transformation.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as input.\n                    The first 4 columns contain the transformed coordinates, and any\n                    additional columns are preserved from the input.\n\n    Note:\n        - This function modifies only the coordinate columns (first 4) of the input bounding boxes.\n        - Any additional attributes (columns beyond the first 4) are kept unchanged.\n        - The function handles denormalization and renormalization of coordinates internally.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n        >>> image_shape = (100, 100)\n        >>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n        >>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n    \"\"\"\n    height, width = image_shape[:2]\n    transformed_bboxes = bboxes.copy()\n    denormalized_coords = denormalize_bboxes(bboxes[:, :4], image_shape)\n\n    x_min, y_min, x_max, y_max = denormalized_coords.T\n    points = np.array(\n        [[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]],\n    ).transpose(2, 0, 1)\n    points_reshaped = points.reshape(-1, 1, 2)\n\n    transformed_points = cv2.perspectiveTransform(\n        points_reshaped.astype(np.float32),\n        matrix,\n    )\n    transformed_points = transformed_points.reshape(-1, 4, 2)\n\n    new_coords = np.array(\n        [[np.min(box[:, 0]), np.min(box[:, 1]), np.max(box[:, 0]), np.max(box[:, 1])] for box in transformed_points],\n    )\n\n    if keep_size:\n        scale_x, scale_y = width / max_width, height / max_height\n        new_coords[:, [0, 2]] *= scale_x\n        new_coords[:, [1, 3]] *= scale_y\n        output_shape = image_shape\n    else:\n        output_shape = (max_height, max_width)\n\n    normalized_coords = normalize_bboxes(new_coords, output_shape)\n    transformed_bboxes[:, :4] = normalized_coords\n\n    return transformed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.rotation2d_matrix_to_euler_angles","title":"def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]","text":"

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:\n    \"\"\"Args:\n    matrix (np.ndarray): Rotation matrix\n    y_up (bool): is Y axis looks up or down\n\n    \"\"\"\n    if y_up:\n        return np.arctan2(matrix[1, 0], matrix[0, 0])\n    return np.arctan2(-matrix[1, 0], matrix[0, 0])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.shift_bboxes","title":"def shift_bboxes (bboxes, shift_vector) [view source on GitHub]","text":"

Shift bounding boxes by a given vector.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Shift bounding boxes by a given vector.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where n is the number of bboxes\n                             and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].\n        shift_vector (np.ndarray): Vector to shift the bounding boxes by, with shape (4,) for\n                                   [shift_x, shift_y, shift_x, shift_y].\n\n    Returns:\n        np.ndarray: Shifted bounding boxes with the same shape as input.\n    \"\"\"\n    # Create a copy of the input array to avoid modifying it in-place\n    shifted_bboxes = bboxes.copy()\n\n    # Add the shift vector to the first 4 columns\n    shifted_bboxes[:, :4] += shift_vector\n\n    return shifted_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.shuffle_tiles_within_shape_groups","title":"def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]","text":"

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py Python
def shuffle_tiles_within_shape_groups(\n    shape_groups: dict[tuple[int, int], list[int]],\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Shuffles indices within each group of similar shapes and creates a list where each\n    index points to the index of the tile it should be mapped to.\n\n    Args:\n        shape_groups (dict[tuple[int, int], list[int]]): Groups of tile indices categorized by shape.\n        random_generator (np.random.Generator): The random generator to use for shuffling the indices.\n            If None, a new random generator will be used.\n\n    Returns:\n        list[int]: A list where each index is mapped to the new index of the tile after shuffling.\n    \"\"\"\n    # Initialize the output list with the same size as the total number of tiles, filled with -1\n    num_tiles = sum(len(indices) for indices in shape_groups.values())\n    mapping = [-1] * num_tiles\n\n    # Prepare the random number generator\n\n    for indices in shape_groups.values():\n        shuffled_indices = indices.copy()\n        random_generator.shuffle(shuffled_indices)\n\n        for old, new in zip(indices, shuffled_indices):\n            mapping[old] = new\n\n    return mapping\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.split_uniform_grid","title":"def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]","text":"

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py Python
def split_uniform_grid(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Splits an image shape into a uniform grid specified by the grid dimensions.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns).\n        random_generator (np.random.Generator): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).\n\n    Note:\n        The function uses `generate_shuffled_splits` to generate the splits for the height and width of the image.\n        The splits are then used to calculate the coordinates of the tiles.\n    \"\"\"\n    n_rows, n_cols = grid\n\n    height_splits = generate_shuffled_splits(\n        image_shape[0],\n        grid[0],\n        random_generator=random_generator,\n    )\n    width_splits = generate_shuffled_splits(\n        image_shape[1],\n        grid[1],\n        random_generator=random_generator,\n    )\n\n    # Calculate tiles coordinates\n    tiles = [\n        (height_splits[i], width_splits[j], height_splits[i + 1], width_splits[j + 1])\n        for i in range(n_rows)\n        for j in range(n_cols)\n    ]\n\n    return np.array(tiles, dtype=np.int16)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.swap_tiles_on_image","title":"def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]","text":"

Swap tiles on the image according to the new format.

Parameters:

Name Type Description image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_image(\n    image: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int] | None = None,\n) -> np.ndarray:\n    \"\"\"Swap tiles on the image according to the new format.\n\n    Args:\n        image: Input image.\n        tiles: Array of tiles with each tile as [start_y, start_x, end_y, end_x].\n        mapping: list of new tile indices.\n\n    Returns:\n        np.ndarray: Output image with tiles swapped according to the random shuffle.\n    \"\"\"\n    # If no tiles are provided, return a copy of the original image\n    if tiles.size == 0 or mapping is None:\n        return image.copy()\n\n    # Create a copy of the image to retain original for reference\n    new_image = np.empty_like(image)\n    for num, new_index in enumerate(mapping):\n        start_y, start_x, end_y, end_x = tiles[new_index]\n        start_y_orig, start_x_orig, end_y_orig, end_x_orig = tiles[num]\n        # Assign the corresponding tile from the original image to the new image\n        new_image[start_y:end_y, start_x:end_x] = image[\n            start_y_orig:end_y_orig,\n            start_x_orig:end_x_orig,\n        ]\n\n    return new_image\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.swap_tiles_on_keypoints","title":"def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]","text":"

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_keypoints(\n    keypoints: np.ndarray,\n    tiles: np.ndarray,\n    mapping: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Swap the positions of keypoints based on a tile mapping.\n\n    This function takes a set of keypoints and repositions them according to a mapping of tile swaps.\n    Keypoints are moved from their original tiles to new positions in the swapped tiles.\n\n    Args:\n        keypoints (np.ndarray): A 2D numpy array of shape (N, 2) where N is the number of keypoints.\n                                Each row represents a keypoint's (x, y) coordinates.\n        tiles (np.ndarray): A 2D numpy array of shape (M, 4) where M is the number of tiles.\n                            Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.\n        mapping (np.ndarray): A 1D numpy array of shape (M,) where M is the number of tiles.\n                              Each element i contains the index of the tile that tile i should be swapped with.\n\n    Returns:\n        np.ndarray: A 2D numpy array of the same shape as the input keypoints, containing the new positions\n                    of the keypoints after the tile swap.\n\n    Raises:\n        RuntimeWarning: If any keypoint is not found within any tile.\n\n    Notes:\n        - Keypoints that do not fall within any tile will remain unchanged.\n        - The function assumes that the tiles do not overlap and cover the entire image space.\n    \"\"\"\n    if not keypoints.size:\n        return keypoints\n\n    # Broadcast keypoints and tiles for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    start_y, start_x, end_y, end_x = tiles.T  # Each shape: (num_tiles,)\n\n    # Check if each keypoint is inside each tile\n    in_tile = (kp_y >= start_y) & (kp_y < end_y) & (kp_x >= start_x) & (kp_x < end_x)\n\n    # Find which tile each keypoint belongs to\n    tile_indices = np.argmax(in_tile, axis=1)\n\n    # Check if any keypoint is not in any tile\n    not_in_any_tile = ~np.any(in_tile, axis=1)\n    if np.any(not_in_any_tile):\n        warn(\n            \"Some keypoints are not in any tile. They will be returned unchanged. This is unexpected and should be \"\n            \"investigated.\",\n            RuntimeWarning,\n            stacklevel=2,\n        )\n\n    # Get the new tile indices\n    new_tile_indices = np.array(mapping)[tile_indices]\n\n    # Calculate the offsets\n    old_start_x = tiles[tile_indices, 1]\n    old_start_y = tiles[tile_indices, 0]\n    new_start_x = tiles[new_tile_indices, 1]\n    new_start_y = tiles[new_tile_indices, 0]\n\n    # Apply the transformation\n    new_keypoints = keypoints.copy()\n    new_keypoints[:, 0] = (keypoints[:, 0] - old_start_x) + new_start_x\n    new_keypoints[:, 1] = (keypoints[:, 1] - old_start_y) + new_start_y\n\n    # Keep original coordinates for keypoints not in any tile\n    new_keypoints[not_in_any_tile] = keypoints[not_in_any_tile]\n\n    return new_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.to_distance_maps","title":"def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]","text":"

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def to_distance_maps(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    inverted: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate a ``(H,W,N)`` array of distance maps for ``N`` keypoints.\n\n    The ``n``-th distance map contains at every location ``(y, x)`` the\n    euclidean distance to the ``n``-th keypoint.\n\n    This function can be used as a helper when augmenting keypoints with a\n    method that only supports the augmentation of images.\n\n    Args:\n        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.\n                   Each row represents a keypoint's (x, y) coordinates.\n        image_shape: tuple[int, int] shape of the image (height, width)\n        inverted (bool): If ``True``, inverted distance maps are returned where each\n            distance value d is replaced by ``d/(d+1)``, i.e. the distance\n            maps have values in the range ``(0.0, 1.0]`` with ``1.0`` denoting\n            exactly the position of the respective keypoint.\n\n    Returns:\n        np.ndarray: A ``float32`` array of shape (H, W, N) containing ``N`` distance maps for ``N``\n            keypoints. Each location ``(y, x, n)`` in the array denotes the\n            euclidean distance at ``(y, x)`` to the ``n``-th keypoint.\n            If `inverted` is ``True``, the distance ``d`` is replaced\n            by ``d/(d+1)``. The height and width of the array match the\n            height and width in ``image_shape``.\n    \"\"\"\n    height, width = image_shape[:2]\n    if len(keypoints) == 0:\n        return np.zeros((height, width, 0), dtype=np.float32)\n\n    # Create coordinate grids\n    yy, xx = np.mgrid[:height, :width]\n\n    # Convert keypoints to numpy array\n    keypoints_array = np.array(keypoints)\n\n    # Compute distances for all keypoints at once\n    distances = np.sqrt(\n        (xx[..., np.newaxis] - keypoints_array[:, 0]) ** 2 + (yy[..., np.newaxis] - keypoints_array[:, 1]) ** 2,\n    )\n\n    if inverted:\n        return (1 / (distances + 1)).astype(np.float32)\n    return distances.astype(np.float32)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.tps_transform","title":"def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]","text":"

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def tps_transform(\n    target_points: np.ndarray,\n    control_points: np.ndarray,\n    nonlinear_weights: np.ndarray,\n    affine_weights: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply Thin Plate Spline transformation to points.\n\n    Args:\n        target_points: Points to transform with shape (num_targets, 2)\n        control_points: Original control points with shape (num_controls, 2)\n        nonlinear_weights: TPS kernel weights with shape (num_controls, 2)\n        affine_weights: Affine transformation weights with shape (3, 2)\n\n    Returns:\n        Transformed points with shape (num_targets, 2)\n\n    Note:\n        The transformation combines:\n        1. Nonlinear warping based on distances to control points\n        2. Global affine transformation (scale, rotation, translation)\n    \"\"\"\n    # Compute all pairwise distances at once: (num_targets, num_controls)\n    distances = np.linalg.norm(target_points[:, None] - control_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Prepare affine terms [1, x, y] for each point\n    affine_terms = np.c_[np.ones(len(target_points)), target_points]\n\n    # Combine nonlinear and affine transformations\n    return kernel_matrix @ nonlinear_weights + affine_terms @ affine_weights\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.transpose","title":"def transpose (img) [view source on GitHub]","text":"

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description img np.ndarray

Input array.

Returns:

Type Description np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py Python
def transpose(img: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes the first two dimensions of an array of any dimensionality.\n    Retains the order of any additional dimensions.\n\n    Args:\n        img (np.ndarray): Input array.\n\n    Returns:\n        np.ndarray: Transposed array.\n    \"\"\"\n    # Generate the new axes order\n    new_axes = list(range(img.ndim))\n    new_axes[0], new_axes[1] = 1, 0  # Swap the first two dimensions\n\n    # Transpose the array using the new axes order\n    return img.transpose(new_axes)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_bboxes","title":"def validate_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n>>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n>>> print(valid_bboxes)\n[[10 20 30 40]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
def validate_bboxes(bboxes: np.ndarray, image_shape: Sequence[int]) -> np.ndarray:\n    \"\"\"Validate bounding boxes and remove invalid ones.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid bounding boxes, potentially with fewer boxes than the input.\n\n    Example:\n        >>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n        >>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n        >>> print(valid_bboxes)\n        [[10 20 30 40]]\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    valid_indices = (x_max > 0) & (y_max > 0) & (x_min < cols) & (y_min < rows)\n\n    return bboxes[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_if_not_found_coords","title":"def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]","text":"

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_if_not_found_coords(\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None,\n) -> tuple[bool, float, float]:\n    \"\"\"Validate and process `if_not_found_coords` parameter.\"\"\"\n    if if_not_found_coords is None:\n        return True, -1, -1\n    if isinstance(if_not_found_coords, (tuple, list)):\n        if len(if_not_found_coords) != PAIR:\n            msg = \"Expected tuple/list 'if_not_found_coords' to contain exactly two entries.\"\n            raise ValueError(msg)\n        return False, if_not_found_coords[0], if_not_found_coords[1]\n    if isinstance(if_not_found_coords, dict):\n        return False, if_not_found_coords[\"x\"], if_not_found_coords[\"y\"]\n\n    msg = \"Expected if_not_found_coords to be None, tuple, list, or dict.\"\n    raise ValueError(msg)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_keypoints","title":"def validate_keypoints (keypoints, image_shape) [view source on GitHub]","text":"

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Validate keypoints and remove those that fall outside the image boundaries.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, M) where N is the number of keypoints\n                                and M >= 2. The first two columns represent x and y coordinates.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid keypoints that fall within the image boundaries.\n\n    Note:\n        This function only checks the x and y coordinates (first two columns) of the keypoints.\n        Any additional columns (e.g., angle, scale) are preserved for valid keypoints.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x, y = keypoints[:, 0], keypoints[:, 1]\n\n    valid_indices = (x >= 0) & (x < cols) & (y >= 0) & (y < rows)\n\n    return keypoints[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize","title":"resize","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.LongestMaxSize","title":"class LongestMaxSize [view source on GitHub]","text":"

Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, Sequence[int]

Maximum size of the longest side after the transformation. When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must fit within these bounds - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

interpolation method. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the longest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / max(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = min(H_target/H, W_target/W)\n       This ensures both dimensions fit within the specified bounds.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> # Using max_size\n>>> transform1 = A.LongestMaxSize(max_size=1024)\n>>> # Input image (1500, 800) -> Output (1024, 546)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>> # Input (800, 1500) -> Output (546, 1024)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>>\n>>> # Common use case with padding\n>>> transform4 = A.Compose([\n...     A.LongestMaxSize(max_size=1024),\n...     A.PadIfNeeded(min_height=1024, min_width=1024),\n... ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class LongestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints,\n        keeping the aspect ratio.\n\n    Args:\n        max_size (int, Sequence[int], optional): Maximum size of the longest side after the transformation.\n            When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must fit within these bounds\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): interpolation method. Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the longest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / max(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = min(H_target/H, W_target/W)\n               This ensures both dimensions fit within the specified bounds.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> # Using max_size\n        >>> transform1 = A.LongestMaxSize(max_size=1024)\n        >>> # Input image (1500, 800) -> Output (1024, 546)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>> # Input (800, 1500) -> Output (546, 1024)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>>\n        >>> # Common use case with padding\n        >>> transform4 = A.Compose([\n        ...     A.LongestMaxSize(max_size=1024),\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024),\n        ... ])\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / max(img_h, img_w)\n        elif self.max_size_hw is not None:\n            # We know max_size_hw is not None here due to model validator\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on longest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = min(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.MaxSizeTransform","title":"class MaxSizeTransform (max_size=1024, max_size_hw=None, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that resize based on maximum size constraints.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class MaxSizeTransform(DualTransform):\n    \"\"\"Base class for transforms that resize based on maximum size constraints.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_size: int | list[int] | None\n        max_size_hw: tuple[int | None, int | None] | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def validate_size_parameters(self) -> Self:\n            if self.max_size is None and self.max_size_hw is None:\n                raise ValueError(\"Either max_size or max_size_hw must be specified\")\n            if self.max_size is not None and self.max_size_hw is not None:\n                raise ValueError(\"Only one of max_size or max_size_hw should be specified\")\n            return self\n\n    def __init__(\n        self,\n        max_size: int | Sequence[int] | None = 1024,\n        max_size_hw: tuple[int | None, int | None] | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_size = max_size\n        self.max_size_hw = max_size_hw\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = img.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(img, (new_height, new_width), interpolation=self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = mask.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(mask, (new_height, new_width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(images, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volume, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volumes, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(masks3d, *args, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_size\", \"max_size_hw\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.RandomScale","title":"class RandomScale (scale_limit=(-0.1, 0.1), interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly resize the input. Output image size is different from the input image size.

Parameters:

Name Type Description scale_limit float or tuple[float, float]

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The output image size is different from the input image size.
  • Scale factor is sampled independently per image side (width and height).
  • Bounding box coordinates are scaled accordingly.
  • Keypoint coordinates are scaled accordingly.

Mathematical formulation: Let (W, H) be the original image dimensions and (W', H') be the output dimensions. The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]]. Then, W' = W * s and H' = H * s.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> scaled_image = result['image']\n# scaled_image will have dimensions in the range [90, 110] x [90, 110]\n# (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class RandomScale(DualTransform):\n    \"\"\"Randomly resize the input. Output image size is different from the input image size.\n\n    Args:\n        scale_limit (float or tuple[float, float]): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The output image size is different from the input image size.\n        - Scale factor is sampled independently per image side (width and height).\n        - Bounding box coordinates are scaled accordingly.\n        - Keypoint coordinates are scaled accordingly.\n\n    Mathematical formulation:\n        Let (W, H) be the original image dimensions and (W', H') be the output dimensions.\n        The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]].\n        Then, W' = W * s and H' = H * s.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> scaled_image = result['image']\n        # scaled_image will have dimensions in the range [90, 110] x [90, 110]\n        # (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_limit: ScaleFloatType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(cls, v: ScaleFloatType) -> tuple[float, float]:\n            return to_tuple(v, bias=1.0)\n\n    def __init__(\n        self,\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def get_params(self) -> dict[str, float]:\n        return {\"scale\": self.py_random.uniform(*self.scale_limit)}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(img, scale, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(mask, scale, self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"interpolation\": self.interpolation,\n            \"mask_interpolation\": self.mask_interpolation,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.Resize","title":"class Resize (height, width, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Resize the input to the given height and width.

Parameters:

Name Type Description height int

desired height of the output.

width int

desired width of the output.

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class Resize(DualTransform):\n    \"\"\"Resize the input to the given height and width.\n\n    Args:\n        height (int): desired height of the output.\n        width (int): desired width of the output.\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: int = Field(ge=1)\n        width: int = Field(ge=1)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(img, (self.height, self.width), interpolation=self.interpolation)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(mask, (self.height, self.width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        height, width = params[\"shape\"][:2]\n        scale_x = self.width / width\n        scale_y = self.height / height\n        return fgeometric.keypoints_scale(keypoints, scale_x, scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.SmallestMaxSize","title":"class SmallestMaxSize [view source on GitHub]","text":"

Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, list of int

Maximum size of smallest side of the image after the transformation. When using a list, max size will be randomly selected from the values in the list. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must be at least these values - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the smallest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be larger than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / min(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = max(H_target/H, W_target/W)\n       This ensures both dimensions are at least as large as specified.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Using max_size\n>>> transform1 = A.SmallestMaxSize(max_size=120)\n>>> # Input image (100, 150) -> Output (120, 180)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n>>> # Input (80, 160) -> Output (100, 200)\n>>> # Input (160, 80) -> Output (400, 200)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n>>> # Input (80, 160) -> Output (100, 200)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class SmallestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints,\n    keeping the aspect ratio.\n\n    Args:\n        max_size (int, list of int, optional): Maximum size of smallest side of the image after the transformation.\n            When using a list, max size will be randomly selected from the values in the list. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must be at least these values\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the smallest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be larger than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / min(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = max(H_target/H, W_target/W)\n               This ensures both dimensions are at least as large as specified.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Using max_size\n        >>> transform1 = A.SmallestMaxSize(max_size=120)\n        >>> # Input image (100, 150) -> Output (120, 180)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n        >>> # Input (80, 160) -> Output (100, 200)\n        >>> # Input (160, 80) -> Output (400, 200)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n        >>> # Input (80, 160) -> Output (100, 200)\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / min(img_h, img_w)\n        elif self.max_size_hw is not None:\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on smallest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = max(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate","title":"rotate","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.RandomRotate90","title":"class RandomRotate90 [view source on GitHub]","text":"

Randomly rotate the input by 90 degrees zero or more times.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RandomRotate90(DualTransform):\n    \"\"\"Randomly rotate the input by 90 degrees zero or more times.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, factor: int, **params: Any) -> np.ndarray:\n        return fgeometric.rot90(img, factor)\n\n    def get_params(self) -> dict[str, int]:\n        # Random int in the range [0, 3]\n        return {\"factor\": self.py_random.randint(0, 3)}\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        factor: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_rot90(bboxes, factor)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        factor: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_rot90(keypoints, factor, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.Rotate","title":"class Rotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', crop_border=False, mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input by an angle selected randomly from the uniform distribution.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method str

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

crop_border bool

Whether to crop border after rotation. If True, the output image size might differ from the input. Default: False

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation angle is randomly selected for each execution within the range specified by 'limit'.
  • When 'crop_border' is False, the output image will have the same size as the input, potentially introducing black triangles in the corners.
  • When 'crop_border' is True, the output image is cropped to remove black triangles, which may result in a smaller image.
  • Bounding boxes are rotated and may change size or shape.
  • Keypoints are rotated around the center of the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. Each point (x, y) in the image is transformed to (x', y') by: [x'] cos(\u03b8) -sin(\u03b8) [cx] [y'] = sin(\u03b8) cos(\u03b8) + [cy] where (cx, cy) is the center of the image. 5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Rotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class Rotate(DualTransform):\n    \"\"\"Rotate the input by an angle selected randomly from the uniform distribution.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        rotate_method (str): Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'.\n            Default: 'largest_box'\n        crop_border (bool): Whether to crop border after rotation. If True, the output image size might differ\n            from the input. Default: False\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation angle is randomly selected for each execution within the range specified by 'limit'.\n        - When 'crop_border' is False, the output image will have the same size as the input, potentially\n          introducing black triangles in the corners.\n        - When 'crop_border' is True, the output image is cropped to remove black triangles, which may result\n          in a smaller image.\n        - Bounding boxes are rotated and may change size or shape.\n        - Keypoints are rotated around the center of the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [cos(\u03b8)  -sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [sin(\u03b8)   cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n        5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Rotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        crop_border: bool\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_value(self) -> Self:\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        crop_border: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.crop_border = crop_border\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            img,\n            matrix,\n            self.interpolation,\n            self.fill,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            mask,\n            matrix,\n            self.mask_interpolation,\n            self.fill_mask,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_out = fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            image_shape,\n            self.border_mode,\n            image_shape,\n        )\n        if self.crop_border:\n            return fcrops.crop_bboxes_by_coords(\n                bboxes_out,\n                (x_min, y_min, x_max, y_max),\n                image_shape,\n            )\n        return bboxes_out\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints_out = fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"][:2],\n            scale={\"x\": 1, \"y\": 1},\n            border_mode=self.border_mode,\n        )\n        if self.crop_border:\n            return fcrops.crop_keypoints_by_coords(\n                keypoints_out,\n                (x_min, y_min, x_max, y_max),\n            )\n        return keypoints_out\n\n    @staticmethod\n    def _rotated_rect_with_max_area(\n        height: int,\n        width: int,\n        angle: float,\n    ) -> dict[str, int]:\n        \"\"\"Given a rectangle of size wxh that has been rotated by 'angle' (in\n        degrees), computes the width and height of the largest possible\n        axis-aligned rectangle (maximal area) within the rotated rectangle.\n\n        Reference:\n            https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders\n        \"\"\"\n        angle = math.radians(angle)\n        width_is_longer = width >= height\n        side_long, side_short = (width, height) if width_is_longer else (height, width)\n\n        # since the solutions for angle, -angle and 180-angle are all the same,\n        # it is sufficient to look at the first quadrant and the absolute values of sin,cos:\n        sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))\n        if side_short <= 2.0 * sin_a * cos_a * side_long or abs(sin_a - cos_a) < SMALL_NUMBER:\n            # half constrained case: two crop corners touch the longer side,\n            # the other two corners are on the mid-line parallel to the longer line\n            x = 0.5 * side_short\n            wr, hr = (x / sin_a, x / cos_a) if width_is_longer else (x / cos_a, x / sin_a)\n        else:\n            # fully constrained case: crop touches all 4 sides\n            cos_2a = cos_a * cos_a - sin_a * sin_a\n            wr, hr = (\n                (width * cos_a - height * sin_a) / cos_2a,\n                (height * cos_a - width * sin_a) / cos_2a,\n            )\n\n        return {\n            \"x_min\": max(0, int(width / 2 - wr / 2)),\n            \"x_max\": min(width, int(width / 2 + wr / 2)),\n            \"y_min\": max(0, int(height / 2 - hr / 2)),\n            \"y_max\": min(height, int(height / 2 + hr / 2)),\n        }\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        angle = self.py_random.uniform(*self.limit)\n\n        if self.crop_border:\n            height, width = params[\"shape\"][:2]\n            out_params = self._rotated_rect_with_max_area(height, width, angle)\n        else:\n            out_params = {\"x_min\": -1, \"x_max\": -1, \"y_min\": -1, \"y_max\": -1}\n\n        center = fgeometric.center(params[\"shape\"][:2])\n        bbox_center = fgeometric.center_bbox(params[\"shape\"][:2])\n\n        translate: fgeometric.XYInt = {\"x\": 0, \"y\": 0}\n        shear: fgeometric.XYFloat = {\"x\": 0, \"y\": 0}\n        scale: fgeometric.XYFloat = {\"x\": 1, \"y\": 1}\n        rotate = angle\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            center,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_center,\n        )\n        out_params[\"matrix\"] = matrix\n        out_params[\"bbox_matrix\"] = bbox_matrix\n\n        return out_params\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"crop_border\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.RotateInitSchema","title":"class RotateInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RotateInitSchema(BaseTransformInitSchema):\n    limit: SymmetricRangeType\n\n    interpolation: InterpolationType\n    mask_interpolation: InterpolationType\n\n    border_mode: BorderModeType\n\n    fill: ColorType | None\n    fill_mask: ColorType | None\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.SafeRotate","title":"class SafeRotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.

This transformation ensures that the entire rotated image fits within the original frame by scaling it down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the rotation and scaling process.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method Literal[\"largest_box\", \"ellipse\"]

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation is performed around the center of the image.
  • After rotation, the image is scaled to fit within the original frame, which may cause some distortion.
  • The output image will always have the same dimensions as the input image.
  • Bounding boxes and keypoints are transformed along with the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. The scaling factor s is calculated to ensure the rotated image fits within the original frame: s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|), height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|)) 5. The combined transformation matrix T is: T = [scos(\u03b8) -ssin(\u03b8) tx] [ssin(\u03b8) scos(\u03b8) ty] where tx and ty are translation factors to keep the image centered. 6. Each point (x, y) in the image is transformed to (x', y') by: [x'] scos(\u03b8) ssin(\u03b8) [cx] [y'] = -ssin(\u03b8) scos(\u03b8) + [cy] where (cx, cy) is the center of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.SafeRotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n# scaled to fit within the original 100x100 frame\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class SafeRotate(Affine):\n    \"\"\"Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.\n\n    This transformation ensures that the entire rotated image fits within the original frame by scaling it\n    down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the\n    rotation and scaling process.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied\n            for masks.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): Method to rotate bounding boxes.\n            Should be 'largest_box' or 'ellipse'. Default: 'largest_box'\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation is performed around the center of the image.\n        - After rotation, the image is scaled to fit within the original frame, which may cause some distortion.\n        - The output image will always have the same dimensions as the input image.\n        - Bounding boxes and keypoints are transformed along with the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. The scaling factor s is calculated to ensure the rotated image fits within the original frame:\n           s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|),\n                   height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|))\n        5. The combined transformation matrix T is:\n           T = [s*cos(\u03b8)  -s*sin(\u03b8)  tx]\n               [s*sin(\u03b8)   s*cos(\u03b8)  ty]\n           where tx and ty are translation factors to keep the image centered.\n        6. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [s*cos(\u03b8)   s*sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [-s*sin(\u03b8)  s*cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.SafeRotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n        # scaled to fit within the original 100x100 frame\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            rotate=limit,\n            interpolation=interpolation,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            rotate_method=rotate_method,\n            fit_output=True,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.mask_interpolation = mask_interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"mask_interpolation\",\n        )\n\n    def _create_safe_rotate_matrix(\n        self,\n        angle: float,\n        center: tuple[float, float],\n        image_shape: tuple[int, int],\n    ) -> tuple[np.ndarray, dict[str, float]]:\n        height, width = image_shape[:2]\n        rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.0)\n\n        # Calculate new image size\n        abs_cos = abs(rotation_mat[0, 0])\n        abs_sin = abs(rotation_mat[0, 1])\n        new_w = int(height * abs_sin + width * abs_cos)\n        new_h = int(height * abs_cos + width * abs_sin)\n\n        # Adjust the rotation matrix to take into account the new size\n        rotation_mat[0, 2] += new_w / 2 - center[0]\n        rotation_mat[1, 2] += new_h / 2 - center[1]\n\n        # Calculate scaling factors\n        scale_x = width / new_w\n        scale_y = height / new_h\n\n        # Create scaling matrix\n        scale_mat = np.array([[scale_x, 0, 0], [0, scale_y, 0], [0, 0, 1]])\n\n        # Combine rotation and scaling\n        matrix = scale_mat @ np.vstack([rotation_mat, [0, 0, 1]])\n\n        return matrix, {\"x\": scale_x, \"y\": scale_y}\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        angle = self.py_random.uniform(*self.limit)\n\n        # Calculate centers for image and bbox\n        image_center = fgeometric.center(image_shape)\n        bbox_center = fgeometric.center_bbox(image_shape)\n\n        # Create matrices for image and bbox\n        matrix, scale = self._create_safe_rotate_matrix(\n            angle,\n            image_center,\n            image_shape,\n        )\n        bbox_matrix, _ = self._create_safe_rotate_matrix(\n            angle,\n            bbox_center,\n            image_shape,\n        )\n\n        return {\n            \"rotate\": angle,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": image_shape,\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms","title":"transforms","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Affine","title":"class Affine (scale=1, translate_percent=None, translate_px=None, rotate=0, shear=0, interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, fit_output=False, keep_ratio=False, rotate_method='largest_box', balanced_scale=False, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Augmentation to apply affine transformations to images.

Affine transformations involve:

- Translation (\"move\" image on the x-/y-axis)\n- Rotation\n- Scaling (\"zoom\" in/out)\n- Shear (move one side of the image, turning a square into a trapezoid)\n

All such transformations can create \"new\" pixels in the image without a defined content, e.g. if the image is translated to the left, pixels are created on the right. A method has to be defined to deal with these pixel values. The parameters fill and fill_mask of this class deal with this.

Some transformations involve interpolations between several pixels of the input image to generate output pixel values. The parameters interpolation and mask_interpolation deals with the method of interpolation used for this.

Parameters:

Name Type Description scale number, tuple of number or dict

Scaling factor to use, where 1.0 denotes \"no change\" and 0.5 is zoomed out to 50 percent of the original size. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That the same range will be used for both x- and y-axis. To keep the aspect ratio, set keep_ratio=True, then the same value will be used for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes. Note that when the keep_ratio=True, the x- and y-axis ranges should be the same.

translate_percent None, number, tuple of number or dict

Translation as a fraction of the image height/width (x-translation, y-translation), where 0 denotes \"no change\" and 0.5 denotes \"half of the axis size\". * If None then equivalent to 0.0 unless translate_px has a value other than None. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That sampled fraction value will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

translate_px None, int, tuple of int or dict

Translation in pixels. * If None then equivalent to 0 unless translate_percent has a value other than None. * If a single int, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the discrete interval [a..b]. That number will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

rotate number or tuple of number

Rotation in degrees (NOT radians), i.e. expected value range is around [-360, 360]. Rotation happens around the center of the image, not the top left corner as in some other frameworks. * If a number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b] and used as the rotation value.

shear number, tuple of number or dict

Shear in degrees (NOT radians), i.e. expected value range is around [-360, 360], with reasonable values being in the range of [-45, 45]. * If a number, then that value will be used for all images as the shear on the x-axis (no shear on the y-axis will be done). * If a tuple (a, b), then two value will be uniformly sampled per image from the interval [a, b] and be used as the x- and y-shear value. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

interpolation int

OpenCV interpolation flag.

mask_interpolation int

OpenCV interpolation flag.

fill ColorType

The constant value to use when filling in newly created pixels. (E.g. translating by 1px to the right will create a new 1px-wide column of pixels on the left of the image). The value is only used when mode=constant. The expected value range is [0, 255] for uint8 images.

fill_mask ColorType

Same as fill but only for masks.

border_mode int

OpenCV border flag.

fit_output bool

If True, the image plane size and position will be adjusted to tightly capture the whole image after affine transformation (translate_percent and translate_px are ignored). Otherwise (False), parts of the transformed image may end up outside the image plane. Fitting the output shape can be useful to avoid corners of the image being outside the image plane after applying rotations. Default: False

keep_ratio bool

When True, the original aspect ratio will be kept when the random scale is applied. Default: False.

rotate_method Literal[\"largest_box\", \"ellipse\"]

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"

balanced_scale bool

When True, scaling factors are chosen to be either entirely below or above 1, ensuring balanced scaling. Default: False.

This is important because without it, scaling tends to lean towards upscaling. For example, if we want the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly from [0.5, 2]. With balanced_scale, the function ensures that half the time, the scaling factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in). This makes the zooming in and out process more balanced.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

[1] https://arxiv.org/abs/2109.13488

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Affine(DualTransform):\n    \"\"\"Augmentation to apply affine transformations to images.\n\n    Affine transformations involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a defined content, e.g.\n    if the image is translated to the left, pixels are created on the right.\n    A method has to be defined to deal with these pixel values.\n    The parameters `fill` and `fill_mask` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameters `interpolation` and\n    `mask_interpolation` deals with the method of interpolation used for this.\n\n    Args:\n        scale (number, tuple of number or dict): Scaling factor to use, where ``1.0`` denotes \"no change\" and\n            ``0.5`` is zoomed out to ``50`` percent of the original size.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That the same range will be used for both x- and y-axis. To keep the aspect ratio, set\n                  ``keep_ratio=True``, then the same value will be used for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes. Note that when\n                  the ``keep_ratio=True``, the x- and y-axis ranges should be the same.\n        translate_percent (None, number, tuple of number or dict): Translation as a fraction of the image height/width\n            (x-translation, y-translation), where ``0`` denotes \"no change\"\n            and ``0.5`` denotes \"half of the axis size\".\n                * If ``None`` then equivalent to ``0.0`` unless `translate_px` has a value other than ``None``.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That sampled fraction value will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        translate_px (None, int, tuple of int or dict): Translation in pixels.\n                * If ``None`` then equivalent to ``0`` unless `translate_percent` has a value other than ``None``.\n                * If a single int, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from\n                  the discrete interval ``[a..b]``. That number will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        rotate (number or tuple of number): Rotation in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``. Rotation happens around the *center* of the image,\n            not the top left corner as in some other frameworks.\n                * If a number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``\n                  and used as the rotation value.\n        shear (number, tuple of number or dict): Shear in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``, with reasonable values being in the range of ``[-45, 45]``.\n                * If a number, then that value will be used for all images as\n                  the shear on the x-axis (no shear on the y-axis will be done).\n                * If a tuple ``(a, b)``, then two value will be uniformly sampled per image\n                  from the interval ``[a, b]`` and be used as the x- and y-shear value.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        interpolation (int): OpenCV interpolation flag.\n        mask_interpolation (int): OpenCV interpolation flag.\n        fill (ColorType): The constant value to use when filling in newly created pixels.\n            (E.g. translating by 1px to the right will create a new 1px-wide column of pixels\n            on the left of the image).\n            The value is only used when `mode=constant`. The expected value range is ``[0, 255]`` for ``uint8`` images.\n        fill_mask (ColorType): Same as fill but only for masks.\n        border_mode (int): OpenCV border flag.\n        fit_output (bool): If True, the image plane size and position will be adjusted to tightly capture\n            the whole image after affine transformation (`translate_percent` and `translate_px` are ignored).\n            Otherwise (``False``),  parts of the transformed image may end up outside the image plane.\n            Fitting the output shape can be useful to avoid corners of the image being outside the image plane\n            after applying rotations. Default: False\n        keep_ratio (bool): When True, the original aspect ratio will be kept when the random scale is applied.\n            Default: False.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): rotation method used for the bounding boxes.\n            Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"\n        balanced_scale (bool): When True, scaling factors are chosen to be either entirely below or above 1,\n            ensuring balanced scaling. Default: False.\n\n            This is important because without it, scaling tends to lean towards upscaling. For example, if we want\n            the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is\n            three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly\n            from [0.5, 2]. With `balanced_scale`, the  function ensures that half the time, the scaling\n            factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in).\n            This makes the zooming in and out process more balanced.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        [1] https://arxiv.org/abs/2109.13488\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: ScaleFloatType | fgeometric.XYFloatScale\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None\n        rotate: ScaleFloatType\n        shear: ScaleFloatType | fgeometric.XYFloatScale\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        cval: ColorType | None\n        cval_mask: ColorType | None\n        mode: BorderModeType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        fit_output: bool\n        keep_ratio: bool\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        balanced_scale: bool\n\n        @field_validator(\"shear\", \"scale\")\n        @classmethod\n        def process_shear(\n            cls,\n            value: ScaleFloatType | fgeometric.XYFloatScale,\n            info: ValidationInfo,\n        ) -> fgeometric.XYFloatDict:\n            return cast(\n                fgeometric.XYFloatDict,\n                cls._handle_dict_arg(value, info.field_name),\n            )\n\n        @field_validator(\"rotate\")\n        @classmethod\n        def process_rotate(\n            cls,\n            value: ScaleFloatType,\n        ) -> tuple[float, float]:\n            return to_tuple(value, value)\n\n        @model_validator(mode=\"after\")\n        def handle_translate(self) -> Self:\n            if self.translate_percent is None and self.translate_px is None:\n                self.translate_px = 0\n\n            if self.translate_percent is not None and self.translate_px is not None:\n                msg = \"Expected either translate_percent or translate_px to be provided, but both were provided.\"\n                raise ValueError(msg)\n\n            if self.translate_percent is not None:\n                self.translate_percent = self._handle_dict_arg(\n                    self.translate_percent,\n                    \"translate_percent\",\n                    default=0.0,\n                )  # type: ignore[assignment]\n\n            if self.translate_px is not None:\n                self.translate_px = self._handle_dict_arg(\n                    self.translate_px,\n                    \"translate_px\",\n                    default=0,\n                )  # type: ignore[assignment]\n\n            return self\n\n        @staticmethod\n        def _handle_dict_arg(\n            val: ScaleType | fgeometric.XYFloatScale | fgeometric.XYIntScale,\n            name: str | None,\n            default: float = 1.0,\n        ) -> dict[str, Any]:\n            if isinstance(val, dict):\n                if \"x\" not in val and \"y\" not in val:\n                    raise ValueError(\n                        f'Expected {name} dictionary to contain at least key \"x\" or key \"y\". Found neither of them.',\n                    )\n                x = val.get(\"x\", default)\n                y = val.get(\"y\", default)\n                return {\"x\": to_tuple(x, x), \"y\": to_tuple(y, y)}  # type: ignore[arg-type]\n            return {\"x\": to_tuple(val, val), \"y\": to_tuple(val, val)}\n\n        @model_validator(mode=\"after\")\n        def validate_fill_types(self) -> Self:\n            if self.cval is not None:\n                self.fill = self.cval\n                warn(\"cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n            if self.cval_mask is not None:\n                self.fill_mask = self.cval_mask\n                warn(\"cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n            if self.mode is not None:\n                self.border_mode = self.mode\n                warn(\"mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType | fgeometric.XYFloatScale = 1,\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None = None,\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None = None,\n        rotate: ScaleFloatType = 0,\n        shear: ScaleFloatType | fgeometric.XYFloatScale = 0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: ColorType | None = None,\n        cval_mask: ColorType | None = None,\n        mode: int | None = None,\n        fit_output: bool = False,\n        keep_ratio: bool = False,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        balanced_scale: bool = False,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n        self.scale = cast(fgeometric.XYFloatDict, scale)\n        self.translate_percent = cast(fgeometric.XYFloatDict, translate_percent)\n        self.translate_px = cast(fgeometric.XYIntDict, translate_px)\n        self.rotate = cast(tuple[float, float], rotate)\n        self.fit_output = fit_output\n        self.shear = cast(fgeometric.XYFloatDict, shear)\n        self.keep_ratio = keep_ratio\n        self.rotate_method = rotate_method\n        self.balanced_scale = balanced_scale\n\n        if self.keep_ratio and self.scale[\"x\"] != self.scale[\"y\"]:\n            raise ValueError(\n                f\"When keep_ratio is True, the x and y scale range should be identical. got {self.scale}\",\n            )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"fill\",\n            \"border_mode\",\n            \"scale\",\n            \"translate_percent\",\n            \"translate_px\",\n            \"rotate\",\n            \"fit_output\",\n            \"shear\",\n            \"fill_mask\",\n            \"keep_ratio\",\n            \"rotate_method\",\n            \"balanced_scale\",\n        )\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            img,\n            matrix,\n            interpolation=self.interpolation,\n            fill=self.fill,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            mask,\n            matrix,\n            interpolation=self.mask_interpolation,\n            fill=self.fill_mask,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            params[\"shape\"][:2],\n            self.border_mode,\n            output_shape,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        scale: fgeometric.XYFloat,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"],\n            scale,\n            self.border_mode,\n        )\n\n    @staticmethod\n    def get_scale(\n        scale: fgeometric.XYFloatDict,\n        keep_ratio: bool,\n        balanced_scale: bool,\n        random_state: random.Random,\n    ) -> fgeometric.XYFloat:\n        result_scale = {}\n        for key, value in scale.items():\n            if isinstance(value, (int, float)):\n                result_scale[key] = float(value)\n            elif isinstance(value, tuple):\n                if balanced_scale:\n                    lower_interval = (value[0], 1.0) if value[0] < 1 else None\n                    upper_interval = (1.0, value[1]) if value[1] > 1 else None\n\n                    if lower_interval is not None and upper_interval is not None:\n                        selected_interval = random_state.choice(\n                            [lower_interval, upper_interval],\n                        )\n                    elif lower_interval is not None:\n                        selected_interval = lower_interval\n                    elif upper_interval is not None:\n                        selected_interval = upper_interval\n                    else:\n                        result_scale[key] = 1.0\n                        continue\n\n                    result_scale[key] = random_state.uniform(*selected_interval)\n                else:\n                    result_scale[key] = random_state.uniform(*value)\n            else:\n                raise TypeError(\n                    f\"Invalid scale value for key {key}: {value}. Expected a float or a tuple of two floats.\",\n                )\n\n        if keep_ratio:\n            result_scale[\"y\"] = result_scale[\"x\"]\n\n        return cast(fgeometric.XYFloat, result_scale)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        translate = self._get_translate_params(image_shape)\n        shear = self._get_shear_params()\n        scale = self.get_scale(\n            self.scale,\n            self.keep_ratio,\n            self.balanced_scale,\n            self.py_random,\n        )\n        rotate = self.py_random.uniform(*self.rotate)\n\n        image_shift = fgeometric.center(image_shape)\n        bbox_shift = fgeometric.center_bbox(image_shape)\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            image_shift,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_shift,\n        )\n\n        if self.fit_output:\n            matrix, output_shape = fgeometric.compute_affine_warp_output_shape(\n                matrix,\n                image_shape,\n            )\n            bbox_matrix, _ = fgeometric.compute_affine_warp_output_shape(\n                bbox_matrix,\n                image_shape,\n            )\n        else:\n            output_shape = image_shape\n\n        return {\n            \"rotate\": rotate,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": output_shape,\n        }\n\n    def _get_translate_params(self, image_shape: tuple[int, int]) -> fgeometric.XYInt:\n        height, width = image_shape[:2]\n        if self.translate_px is not None:\n            return {\n                \"x\": self.py_random.randint(*self.translate_px[\"x\"]),\n                \"y\": self.py_random.randint(*self.translate_px[\"y\"]),\n            }\n        if self.translate_percent is not None:\n            translate = {key: self.py_random.uniform(*value) for key, value in self.translate_percent.items()}\n            return cast(\n                fgeometric.XYInt,\n                {\"x\": int(translate[\"x\"] * width), \"y\": int(translate[\"y\"] * height)},\n            )\n        return cast(fgeometric.XYInt, {\"x\": 0, \"y\": 0})\n\n    def _get_shear_params(self) -> fgeometric.XYFloat:\n        return {\n            \"x\": -self.py_random.uniform(*self.shear[\"x\"]),\n            \"y\": -self.py_random.uniform(*self.shear[\"y\"]),\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.BaseDistortion","title":"class BaseDistortion (interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Base class for distortion-based transformations.

This class provides a foundation for implementing various types of image distortions, such as optical distortions, grid distortions, and elastic transformations. It handles the common operations of applying distortions to images, masks, bounding boxes, and keypoints.

Parameters:

Name Type Description interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR, cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This is an abstract base class and should not be used directly.
  • Subclasses should implement the get_params_dependent_on_data method to generate the distortion maps (map_x and map_y).
  • The distortion is applied consistently across all targets (image, mask, bboxes, keypoints) to maintain coherence in the augmented data.

Example of a subclass: class CustomDistortion(BaseDistortion): def init(self, args, **kwargs): super().init(args, **kwargs) # Add custom parameters here

    def get_params_dependent_on_data(self, params, data):\n        # Generate and return map_x and map_y based on the distortion logic\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self):\n        return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class BaseDistortion(DualTransform):\n    \"\"\"Base class for distortion-based transformations.\n\n    This class provides a foundation for implementing various types of image distortions,\n    such as optical distortions, grid distortions, and elastic transformations. It handles\n    the common operations of applying distortions to images, masks, bounding boxes, and keypoints.\n\n    Args:\n        interpolation (int): Interpolation method to be used for image transformation.\n            Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR,\n            cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This is an abstract base class and should not be used directly.\n        - Subclasses should implement the `get_params_dependent_on_data` method to generate\n          the distortion maps (map_x and map_y).\n        - The distortion is applied consistently across all targets (image, mask, bboxes, keypoints)\n          to maintain coherence in the augmented data.\n\n    Example of a subclass:\n        class CustomDistortion(BaseDistortion):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                # Add custom parameters here\n\n            def get_params_dependent_on_data(self, params, data):\n                # Generate and return map_x and map_y based on the distortion logic\n                return {\"map_x\": map_x, \"map_y\": map_y}\n\n            def get_transform_init_args_names(self):\n                return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            img,\n            map_x,\n            map_y,\n            self.interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            mask,\n            map_x,\n            map_y,\n            self.mask_interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        bboxes_returned = fgeometric.remap_bboxes(\n            bboxes_denorm,\n            map_x,\n            map_y,\n            image_shape,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap_keypoints(keypoints, map_x, map_y, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.D4","title":"class D4 (p=1, always_apply=None) [view source on GitHub]","text":"

Applies one of the eight possible D4 dihedral group transformations to a square-shaped input, maintaining the square shape. These transformations correspond to the symmetries of a square, including rotations and reflections.

The D4 group transformations include: - 'e' (identity): No transformation is applied. - 'r90' (rotation by 90 degrees counterclockwise) - 'r180' (rotation by 180 degrees) - 'r270' (rotation by 270 degrees counterclockwise) - 'v' (reflection across the vertical midline) - 'hvt' (reflection across the anti-diagonal) - 'h' (reflection across the horizontal midline) - 't' (reflection across the main diagonal)

Even if the probability (p) of applying the transform is set to 1, the identity transformation 'e' may still occur, which means the input will remain unchanged in one out of eight cases.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for augmenting data that does not have a clear orientation, such as top-view satellite or drone imagery, or certain types of medical images.
  • The input image should be square-shaped for optimal results. Non-square inputs may lead to unexpected behavior or distortions.
  • When applied to bounding boxes or keypoints, their coordinates will be adjusted according to the selected transformation.
  • This transform preserves the aspect ratio and size of the input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.D4(p=1.0),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed['image']\n# The resulting image will be one of the 8 possible D4 transformations of the input\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class D4(DualTransform):\n    \"\"\"Applies one of the eight possible D4 dihedral group transformations to a square-shaped input,\n    maintaining the square shape. These transformations correspond to the symmetries of a square,\n    including rotations and reflections.\n\n    The D4 group transformations include:\n    - 'e' (identity): No transformation is applied.\n    - 'r90' (rotation by 90 degrees counterclockwise)\n    - 'r180' (rotation by 180 degrees)\n    - 'r270' (rotation by 270 degrees counterclockwise)\n    - 'v' (reflection across the vertical midline)\n    - 'hvt' (reflection across the anti-diagonal)\n    - 'h' (reflection across the horizontal midline)\n    - 't' (reflection across the main diagonal)\n\n    Even if the probability (`p`) of applying the transform is set to 1, the identity transformation\n    'e' may still occur, which means the input will remain unchanged in one out of eight cases.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for augmenting data that does not have a clear orientation,\n          such as top-view satellite or drone imagery, or certain types of medical images.\n        - The input image should be square-shaped for optimal results. Non-square inputs may lead to\n          unexpected behavior or distortions.\n        - When applied to bounding boxes or keypoints, their coordinates will be adjusted according\n          to the selected transformation.\n        - This transform preserves the aspect ratio and size of the input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.D4(p=1.0),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed['image']\n        # The resulting image will be one of the 8 possible D4 transformations of the input\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.d4(img, group_element)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_d4(bboxes, group_element)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_d4(keypoints, group_element, params[\"shape\"])\n\n    def get_params(self) -> dict[str, D4Type]:\n        return {\n            \"group_element\": self.random_generator.choice(d4_group_elements),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ElasticTransform","title":"class ElasticTransform (alpha=1, sigma=50, interpolation=1, border_mode=4, value=None, mask_value=None, approximate=False, same_dxdy=False, mask_interpolation=0, noise_distribution='gaussian', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformation to images, masks, bounding boxes, and keypoints.

This transformation introduces random elastic distortions to the input data. It's particularly useful for data augmentation in training deep learning models, especially for tasks like image segmentation or object detection where you want to maintain the relative positions of features while introducing realistic deformations.

The transform works by generating random displacement fields and applying them to the input. These fields are smoothed using a Gaussian filter to create more natural-looking distortions.

Parameters:

Name Type Description alpha float

Scaling factor for the random displacement fields. Higher values result in more pronounced distortions. Default: 1.0

sigma float

Standard deviation of the Gaussian filter used to smooth the displacement fields. Higher values result in smoother, more global distortions. Default: 50.0

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

approximate bool

Whether to use an approximate version of the elastic transform. If True, uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially less accurate for large sigma values. Default: False

same_dxdy bool

Whether to use the same random displacement field for both x and y directions. Can speed up the transform at the cost of less diverse distortions. Default: False

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

noise_distribution Literal[\"gaussian\", \"uniform\"]

Distribution used to generate the displacement fields. \"gaussian\" generates fields using normal distribution (more natural deformations). \"uniform\" generates fields using uniform distribution (more mechanical deformations). Default: \"gaussian\".

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints) by using the same displacement fields for all.
  • The 'approximate' parameter determines whether to use a precise or approximate method for generating displacement fields. The approximate method can be faster but may be less accurate for large sigma values.
  • Bounding boxes that end up outside the image after transformation will be removed.
  • Keypoints that end up outside the image after transformation will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ElasticTransform(BaseDistortion):\n    \"\"\"Apply elastic deformation to images, masks, bounding boxes, and keypoints.\n\n    This transformation introduces random elastic distortions to the input data. It's particularly\n    useful for data augmentation in training deep learning models, especially for tasks like\n    image segmentation or object detection where you want to maintain the relative positions of\n    features while introducing realistic deformations.\n\n    The transform works by generating random displacement fields and applying them to the input.\n    These fields are smoothed using a Gaussian filter to create more natural-looking distortions.\n\n    Args:\n        alpha (float): Scaling factor for the random displacement fields. Higher values result in\n            more pronounced distortions. Default: 1.0\n        sigma (float): Standard deviation of the Gaussian filter used to smooth the displacement\n            fields. Higher values result in smoother, more global distortions. Default: 50.0\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        approximate (bool): Whether to use an approximate version of the elastic transform. If True,\n            uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially\n            less accurate for large sigma values. Default: False\n        same_dxdy (bool): Whether to use the same random displacement field for both x and y\n            directions. Can speed up the transform at the cost of less diverse distortions. Default: False\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        noise_distribution (Literal[\"gaussian\", \"uniform\"]): Distribution used to generate the displacement fields.\n            \"gaussian\" generates fields using normal distribution (more natural deformations).\n            \"uniform\" generates fields using uniform distribution (more mechanical deformations).\n            Default: \"gaussian\".\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints)\n          by using the same displacement fields for all.\n        - The 'approximate' parameter determines whether to use a precise or approximate method for\n          generating displacement fields. The approximate method can be faster but may be less\n          accurate for large sigma values.\n        - Bounding boxes that end up outside the image after transformation will be removed.\n        - Keypoints that end up outside the image after transformation will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        alpha: Annotated[float, Field(ge=0)]\n        sigma: Annotated[float, Field(ge=1)]\n        approximate: bool\n        same_dxdy: bool\n        noise_distribution: Literal[\"gaussian\", \"uniform\"]\n        border_mode: BorderModeType = Field(deprecated=\"Deprecated\")\n        value: ColorType | None = Field(deprecated=\"Deprecated\")\n        mask_value: ColorType | None = Field(deprecated=\"Deprecated\")\n\n    def __init__(\n        self,\n        alpha: float = 1,\n        sigma: float = 50,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        approximate: bool = False,\n        same_dxdy: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        noise_distribution: Literal[\"gaussian\", \"uniform\"] = \"gaussian\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.alpha = alpha\n        self.sigma = sigma\n        self.approximate = approximate\n        self.same_dxdy = same_dxdy\n        self.noise_distribution = noise_distribution\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        kernel_size = (0, 0) if self.approximate else (17, 17)\n\n        # Generate displacement fields\n        dx, dy = fgeometric.generate_displacement_fields(\n            (height, width),\n            self.alpha,\n            self.sigma,\n            same_dxdy=self.same_dxdy,\n            kernel_size=kernel_size,\n            random_generator=self.random_generator,\n            noise_distribution=self.noise_distribution,\n        )\n\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        map_x = np.float32(x + dx)\n        map_y = np.float32(y + dy)\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"alpha\",\n            \"sigma\",\n            \"approximate\",\n            \"same_dxdy\",\n            \"noise_distribution\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.GridDistortion","title":"class GridDistortion (num_steps=5, distort_limit=(-0.3, 0.3), interpolation=1, border_mode=4, value=None, mask_value=None, normalized=True, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply grid distortion to images, masks, bounding boxes, and keypoints.

This transformation divides the image into a grid and randomly distorts each cell, creating localized warping effects. It's particularly useful for data augmentation in tasks like medical image analysis, OCR, and other domains where local geometric variations are meaningful.

Parameters:

Name Type Description num_steps int

Number of grid cells on each side of the image. Higher values create more granular distortions. Must be at least 1. Default: 5.

distort_limit float or tuple[float, float]

Range of distortion. If a single float is provided, the range will be (-distort_limit, distort_limit). Higher values create stronger distortions. Should be in the range of -1 to 1. Default: (-0.3, 0.3).

interpolation int

OpenCV interpolation method used for image transformation. Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.

normalized bool

If True, ensures that the distortion does not move pixels outside the image boundaries. This can result in less extreme distortions but guarantees that no information is lost. Default: True.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The same distortion is applied to all targets (image, mask, bboxes, keypoints) to maintain consistency.
  • When normalized=True, the distortion is adjusted to ensure all pixels remain within the image boundaries.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridDistortion(BaseDistortion):\n    \"\"\"Apply grid distortion to images, masks, bounding boxes, and keypoints.\n\n    This transformation divides the image into a grid and randomly distorts each cell,\n    creating localized warping effects. It's particularly useful for data augmentation\n    in tasks like medical image analysis, OCR, and other domains where local geometric\n    variations are meaningful.\n\n    Args:\n        num_steps (int): Number of grid cells on each side of the image. Higher values\n            create more granular distortions. Must be at least 1. Default: 5.\n        distort_limit (float or tuple[float, float]): Range of distortion. If a single float\n            is provided, the range will be (-distort_limit, distort_limit). Higher values\n            create stronger distortions. Should be in the range of -1 to 1.\n            Default: (-0.3, 0.3).\n        interpolation (int): OpenCV interpolation method used for image transformation.\n            Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.\n        normalized (bool): If True, ensures that the distortion does not move pixels\n            outside the image boundaries. This can result in less extreme distortions\n            but guarantees that no information is lost. Default: True.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The same distortion is applied to all targets (image, mask, bboxes, keypoints)\n          to maintain consistency.\n        - When normalized=True, the distortion is adjusted to ensure all pixels remain\n          within the image boundaries.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        num_steps: Annotated[int, Field(ge=1)]\n        distort_limit: SymmetricRangeType\n        normalized: bool\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int = Field(deprecated=\"Deprecated. Does not have any effect.\")\n\n        @field_validator(\"distort_limit\")\n        @classmethod\n        def check_limits(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = -1, 1\n            result = to_tuple(v)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        num_steps: int = 5,\n        distort_limit: ScaleFloatType = (-0.3, 0.3),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        normalized: bool = True,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.num_steps = num_steps\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.normalized = normalized\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        steps_x = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n        steps_y = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n\n        if self.normalized:\n            normalized_params = fgeometric.normalize_grid_distortion_steps(\n                image_shape,\n                self.num_steps,\n                steps_x,\n                steps_y,\n            )\n            steps_x, steps_y = (\n                normalized_params[\"steps_x\"],\n                normalized_params[\"steps_y\"],\n            )\n\n        map_x, map_y = fgeometric.generate_grid(\n            image_shape,\n            steps_x,\n            steps_y,\n            self.num_steps,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"num_steps\",\n            \"distort_limit\",\n            \"normalized\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.GridElasticDeform","title":"class GridElasticDeform (num_grid_xy, magnitude, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.

This transformation overlays a grid on the input and applies random displacements to the grid points, resulting in local elastic distortions. The granularity and intensity of the distortions can be controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.

Parameters:

Name Type Description num_grid_xy tuple[int, int]

Number of grid cells along the width and height. Specified as (grid_width, grid_height). Each value must be greater than 1.

magnitude int

Maximum pixel-wise displacement for distortion. Must be greater than 0.

interpolation int

Interpolation method to be used for the image transformation. Default: cv2.INTER_LINEAR

mask_interpolation int

Interpolation method to be used for mask transformation. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> transformed_image, transformed_mask = result['image'], result['mask']\n

Note

This transformation is particularly useful for data augmentation in medical imaging and other domains where elastic deformations can simulate realistic variations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridElasticDeform(DualTransform):\n    \"\"\"Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.\n\n    This transformation overlays a grid on the input and applies random displacements to the grid points,\n    resulting in local elastic distortions. The granularity and intensity of the distortions can be\n    controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.\n\n\n    Args:\n        num_grid_xy (tuple[int, int]): Number of grid cells along the width and height.\n            Specified as (grid_width, grid_height). Each value must be greater than 1.\n        magnitude (int): Maximum pixel-wise displacement for distortion. Must be greater than 0.\n        interpolation (int): Interpolation method to be used for the image transformation.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Interpolation method to be used for mask transformation.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = result['image'], result['mask']\n\n    Note:\n        This transformation is particularly useful for data augmentation in medical imaging\n        and other domains where elastic deformations can simulate realistic variations.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        num_grid_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        magnitude: int = Field(gt=0)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        num_grid_xy: tuple[int, int],\n        magnitude: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_grid_xy = num_grid_xy\n        self.magnitude = magnitude\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    @staticmethod\n    def generate_mesh(polygons: np.ndarray, dimensions: np.ndarray) -> np.ndarray:\n        return np.hstack((dimensions.reshape(-1, 4), polygons))\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        # Replace calculate_grid_dimensions with split_uniform_grid\n        tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.num_grid_xy,\n            self.random_generator,\n        )\n\n        # Convert tiles to the format expected by generate_distorted_grid_polygons\n        dimensions = np.array(\n            [\n                [\n                    tile[1],\n                    tile[0],\n                    tile[3],\n                    tile[2],\n                ]  # Reorder to [x_min, y_min, x_max, y_max]\n                for tile in tiles\n            ],\n        ).reshape(\n            self.num_grid_xy[::-1] + (4,),\n        )  # Reshape to (grid_height, grid_width, 4)\n\n        polygons = fgeometric.generate_distorted_grid_polygons(\n            dimensions,\n            self.magnitude,\n            self.random_generator,\n        )\n\n        generated_mesh = self.generate_mesh(polygons, dimensions)\n\n        return {\"generated_mesh\": generated_mesh}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(img, generated_mesh, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(mask, generated_mesh, self.mask_interpolation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        bboxes_denorm = denormalize_bboxes(bboxes, params[\"shape\"][:2])\n        return normalize_bboxes(\n            fgeometric.bbox_distort_image(\n                bboxes_denorm,\n                generated_mesh,\n                params[\"shape\"][:2],\n            ),\n            params[\"shape\"][:2],\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image_keypoints(\n            keypoints,\n            generated_mesh,\n            params[\"shape\"][:2],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_grid_xy\", \"magnitude\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.HorizontalFlip","title":"class HorizontalFlip [view source on GitHub]","text":"

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class HorizontalFlip(DualTransform):\n    \"\"\"Flip the input horizontally around the y-axis.\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return hflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_hflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_hflip(keypoints, params[\"shape\"][1])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.OpticalDistortion","title":"class OpticalDistortion (distort_limit=(-0.05, 0.05), shift_limit=None, interpolation=1, border_mode=None, value=None, mask_value=None, mask_interpolation=0, mode='camera', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply optical distortion to images, masks, bounding boxes, and keypoints.

Supports two distortion models: 1. Camera matrix model (original): Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients

  1. Fisheye model: Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)

Parameters:

Name Type Description distort_limit float | tuple[float, float]

Range of distortion coefficient. For camera model: recommended range (-0.05, 0.05) For fisheye model: recommended range (-0.3, 0.3) Default: (-0.05, 0.05)

mode Literal['camera', 'fisheye']

Distortion model to use: - 'camera': Original camera matrix model - 'fisheye': Fisheye lens model Default: 'camera'

interpolation OpenCV flag

Interpolation method used for image transformation. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.
  • The distortion coefficient (k) is randomly sampled from the distort_limit range.
  • The image center is shifted by dx and dy, randomly sampled from the shift_limit range.
  • Bounding boxes and keypoints are transformed along with the image to maintain consistency.
  • Fisheye model directly applies radial distortion
  • Both models use shift_limit to control distortion center

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class OpticalDistortion(BaseDistortion):\n    \"\"\"Apply optical distortion to images, masks, bounding boxes, and keypoints.\n\n    Supports two distortion models:\n    1. Camera matrix model (original):\n       Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients\n\n    2. Fisheye model:\n       Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)\n\n    Args:\n        distort_limit (float | tuple[float, float]): Range of distortion coefficient.\n            For camera model: recommended range (-0.05, 0.05)\n            For fisheye model: recommended range (-0.3, 0.3)\n            Default: (-0.05, 0.05)\n\n        mode (Literal['camera', 'fisheye']): Distortion model to use:\n            - 'camera': Original camera matrix model\n            - 'fisheye': Fisheye lens model\n            Default: 'camera'\n\n        interpolation (OpenCV flag): Interpolation method used for image transformation.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC,\n            cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.\n        - The distortion coefficient (k) is randomly sampled from the distort_limit range.\n        - The image center is shifted by dx and dy, randomly sampled from the shift_limit range.\n        - Bounding boxes and keypoints are transformed along with the image to maintain consistency.\n        - Fisheye model directly applies radial distortion\n        - Both models use shift_limit to control distortion center\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        distort_limit: SymmetricRangeType\n        mode: Literal[\"camera\", \"fisheye\"]\n        shift_limit: SymmetricRangeType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n    def __init__(\n        self,\n        distort_limit: ScaleFloatType = (-0.05, 0.05),\n        shift_limit: ScaleFloatType | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int | None = None,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        mode: Literal[\"camera\", \"fisheye\"] = \"camera\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.mode = mode\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        height, width = image_shape\n\n        # Get distortion coefficient\n        k = self.py_random.uniform(*self.distort_limit)\n\n        # Calculate center shift\n        center_xy = fgeometric.center(image_shape)\n\n        # Get distortion maps based on mode\n        if self.mode == \"camera\":\n            map_x, map_y = fgeometric.get_camera_matrix_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n        else:  # fisheye\n            map_x, map_y = fgeometric.get_fisheye_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"distort_limit\",\n            \"mode\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Pad","title":"class Pad (padding=0, fill=0, fill_mask=0, border_mode=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of an image by specified number of pixels.

Parameters:

Name Type Description padding int, tuple[int, int] or tuple[int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT

border_mode OpenCV flag

OpenCV border mode

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

References

  • https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Pad(DualTransform):\n    \"\"\"Pad the sides of an image by specified number of pixels.\n\n    Args:\n        padding (int, tuple[int, int] or tuple[int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y\n            * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT\n        fill_mask (ColorType): Padding value for mask if border_mode is cv2.BORDER_CONSTANT\n        border_mode (OpenCV flag): OpenCV border mode\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    References:\n        - https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        padding: int | tuple[int, int] | tuple[int, int, int, int]\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int] | tuple[int, int, int, int] = 0,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            img,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            mask,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill_mask,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_np = denormalize_bboxes(bboxes, params[\"shape\"])\n\n        result = fgeometric.pad_bboxes(\n            bboxes_np,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=image_shape,\n        )\n\n        rows, cols = params[\"shape\"][:2]\n        return normalize_bboxes(\n            result,\n            (rows + pad_top + pad_bottom, cols + pad_left + pad_right),\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_keypoints(\n            keypoints,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=params[\"shape\"][:2],\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if isinstance(self.padding, Real):\n            pad_top = pad_bottom = pad_left = pad_right = self.padding\n        elif isinstance(self.padding, (tuple, list)):\n            if len(self.padding) == NUM_PADS_XY:\n                pad_left = pad_right = self.padding[0]\n                pad_top = pad_bottom = self.padding[1]\n            elif len(self.padding) == NUM_PADS_ALL_SIDES:\n                pad_left, pad_top, pad_right, pad_bottom = self.padding  # type: ignore[misc]\n            else:\n                raise TypeError(\n                    \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n                )\n        else:\n            raise TypeError(\n                \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n            )\n\n        return {\n            \"pad_top\": pad_top,\n            \"pad_bottom\": pad_bottom,\n            \"pad_left\": pad_left,\n            \"pad_right\": pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"padding\",\n            \"fill\",\n            \"fill_mask\",\n            \"border_mode\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.PadIfNeeded","title":"class PadIfNeeded (min_height=1024, min_width=1024, pad_height_divisor=None, pad_width_divisor=None, position='center', border_mode=4, value=None, mask_value=None, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of an image if the image dimensions are less than the specified minimum dimensions. If the pad_height_divisor or pad_width_divisor is specified, the function additionally ensures that the image dimensions are divisible by these values.

Parameters:

Name Type Description min_height int | None

Minimum desired height of the image. Ensures image height is at least this value. If not specified, pad_height_divisor must be provided.

min_width int | None

Minimum desired width of the image. Ensures image width is at least this value. If not specified, pad_width_divisor must be provided.

pad_height_divisor int | None

If set, pads the image height to make it divisible by this value. If not specified, min_height must be provided.

pad_width_divisor int | None

If set, pads the image width to make it divisible by this value. If not specified, min_width must be provided.

position Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]

Position where the image is to be placed after padding. Default is 'center'.

border_mode int

Specifies the border mode to use if padding is required. The default is cv2.BORDER_REFLECT_101.

fill ColorType | None

Value to fill the border pixels if the border mode is cv2.BORDER_CONSTANT. Default is None.

fill_mask ColorType | None

Similar to fill but used for padding masks. Default is None.

p float

Probability of applying the transform. Default is 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • Either min_height or pad_height_divisor must be set, but not both.
  • Either min_width or pad_width_divisor must be set, but not both.
  • If border_mode is set to cv2.BORDER_CONSTANT, value must be provided.
  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).
  • For bounding boxes, the coordinates will be adjusted to account for the padding.
  • For keypoints, their positions will be shifted according to the padding.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> padded_image = transformed['image']\n>>> padded_mask = transformed['mask']\n>>> adjusted_bboxes = transformed['bboxes']\n>>> adjusted_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PadIfNeeded(Pad):\n    \"\"\"Pads the sides of an image if the image dimensions are less than the specified minimum dimensions.\n    If the `pad_height_divisor` or `pad_width_divisor` is specified, the function additionally ensures\n    that the image dimensions are divisible by these values.\n\n    Args:\n        min_height (int | None): Minimum desired height of the image. Ensures image height is at least this value.\n            If not specified, pad_height_divisor must be provided.\n        min_width (int | None): Minimum desired width of the image. Ensures image width is at least this value.\n            If not specified, pad_width_divisor must be provided.\n        pad_height_divisor (int | None): If set, pads the image height to make it divisible by this value.\n            If not specified, min_height must be provided.\n        pad_width_divisor (int | None): If set, pads the image width to make it divisible by this value.\n            If not specified, min_width must be provided.\n        position (Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]):\n            Position where the image is to be placed after padding. Default is 'center'.\n        border_mode (int): Specifies the border mode to use if padding is required.\n            The default is `cv2.BORDER_REFLECT_101`.\n        fill (ColorType | None): Value to fill the border pixels if the border mode is `cv2.BORDER_CONSTANT`.\n            Default is None.\n        fill_mask (ColorType | None): Similar to `fill` but used for padding masks. Default is None.\n        p (float): Probability of applying the transform. Default is 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Either `min_height` or `pad_height_divisor` must be set, but not both.\n        - Either `min_width` or `pad_width_divisor` must be set, but not both.\n        - If `border_mode` is set to `cv2.BORDER_CONSTANT`, `value` must be provided.\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).\n        - For bounding boxes, the coordinates will be adjusted to account for the padding.\n        - For keypoints, their positions will be shifted according to the padding.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> padded_image = transformed['image']\n        >>> padded_mask = transformed['mask']\n        >>> adjusted_bboxes = transformed['bboxes']\n        >>> adjusted_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        min_height: int | None = Field(ge=1)\n        min_width: int | None = Field(ge=1)\n        pad_height_divisor: int | None = Field(ge=1)\n        pad_width_divisor: int | None = Field(ge=1)\n        position: PositionType\n        border_mode: BorderModeType\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        @model_validator(mode=\"after\")\n        def validate_divisibility(self) -> Self:\n            if (self.min_height is None) == (self.pad_height_divisor is None):\n                msg = \"Only one of 'min_height' and 'pad_height_divisor' parameters must be set\"\n                raise ValueError(msg)\n            if (self.min_width is None) == (self.pad_width_divisor is None):\n                msg = \"Only one of 'min_width' and 'pad_width_divisor' parameters must be set\"\n                raise ValueError(msg)\n\n            if self.border_mode == cv2.BORDER_CONSTANT and self.fill is None:\n                msg = \"If 'border_mode' is set to 'BORDER_CONSTANT', 'fill' must be provided.\"\n                raise ValueError(msg)\n\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n\n            return self\n\n    def __init__(\n        self,\n        min_height: int | None = 1024,\n        min_width: int | None = 1024,\n        pad_height_divisor: int | None = None,\n        pad_width_divisor: int | None = None,\n        position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        # Initialize with dummy padding that will be calculated later\n        super().__init__(\n            padding=0,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            p=p,\n        )\n        self.min_height = min_height\n        self.min_width = min_width\n        self.pad_height_divisor = pad_height_divisor\n        self.pad_width_divisor = pad_width_divisor\n        self.position = position\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=params[\"shape\"][:2],\n            min_height=self.min_height,\n            min_width=self.min_width,\n            pad_height_divisor=self.pad_height_divisor,\n            pad_width_divisor=self.pad_width_divisor,\n        )\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_height\",\n            \"min_width\",\n            \"pad_height_divisor\",\n            \"pad_width_divisor\",\n            \"position\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Perspective","title":"class Perspective (scale=(0.05, 0.1), keep_size=True, pad_mode=None, pad_val=None, mask_pad_val=None, fit_output=False, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random four point perspective transformation to the input.

Parameters:

Name Type Description scale float or tuple of float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Default: (0.05, 0.1).

keep_size bool

Whether to resize image back to its original size after applying the perspective transform. If set to False, the resulting images may end up having different shapes. Default: True.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fit_output bool

If True, the image plane size and position will be adjusted to still capture the whole image after perspective transformation. This is followed by image resizing if keep_size is set to True. If False, parts of the transformed image may be outside of the image plane. This setting should not be set to True when using large scale values as it could lead to very large images. Default: False.

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

This transformation creates a perspective effect by randomly moving the four corners of the image. The amount of movement is controlled by the 'scale' parameter.

When 'keep_size' is True, the output image will have the same size as the input image, which may cause some parts of the transformed image to be cut off or padded.

When 'fit_output' is True, the transformation ensures that the entire transformed image is visible, which may result in a larger output image if keep_size is False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n... ])\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Perspective(DualTransform):\n    \"\"\"Apply random four point perspective transformation to the input.\n\n    Args:\n        scale (float or tuple of float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Default: (0.05, 0.1).\n        keep_size (bool): Whether to resize image back to its original size after applying the perspective transform.\n            If set to False, the resulting images may end up having different shapes.\n            Default: True.\n        border_mode (OpenCV flag): OpenCV border mode used for padding.\n            Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n        fill_mask (ColorType): Padding value for mask if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fit_output (bool): If True, the image plane size and position will be adjusted to still capture\n            the whole image after perspective transformation. This is followed by image resizing if keep_size is set\n            to True. If False, parts of the transformed image may be outside of the image plane.\n            This setting should not be set to True when using large scale values as it could lead to very large images.\n            Default: False.\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transformation creates a perspective effect by randomly moving the four corners of the image.\n        The amount of movement is controlled by the 'scale' parameter.\n\n        When 'keep_size' is True, the output image will have the same size as the input image,\n        which may cause some parts of the transformed image to be cut off or padded.\n\n        When 'fit_output' is True, the transformation ensures that the entire transformed image is visible,\n        which may result in a larger output image if keep_size is False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n        ... ])\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        keep_size: bool\n        pad_mode: BorderModeType | None = Field(\n            deprecated=\"Deprecated use border_mode instead\",\n        )\n        pad_val: ColorType | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_pad_val: ColorType | None = Field(\n            deprecated=\"Deprecated use fill_mask instead\",\n        )\n        fit_output: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def validate_deprecated_fields(self) -> Self:\n            if self.pad_mode is not None:\n                self.border_mode = self.pad_mode\n            if self.pad_val is not None:\n                self.fill = self.pad_val\n            if self.mask_pad_val is not None:\n                self.fill_mask = self.mask_pad_val\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.05, 0.1),\n        keep_size: bool = True,\n        pad_mode: int | None = None,\n        pad_val: ColorType | None = None,\n        mask_pad_val: ColorType | None = None,\n        fit_output: bool = False,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply=always_apply)\n        self.scale = cast(tuple[float, float], scale)\n        self.keep_size = keep_size\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.fit_output = fit_output\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            img,\n            matrix,\n            max_width,\n            max_height,\n            self.fill,\n            self.border_mode,\n            self.keep_size,\n            self.interpolation,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            mask,\n            matrix,\n            max_width,\n            max_height,\n            self.fill_mask,\n            self.border_mode,\n            self.keep_size,\n            self.mask_interpolation,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        matrix_bbox: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_bboxes(\n            bboxes,\n            params[\"shape\"],\n            matrix_bbox,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_keypoints(\n            keypoints,\n            params[\"shape\"],\n            matrix,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        scale = self.py_random.uniform(*self.scale)\n\n        points = fgeometric.generate_perspective_points(\n            image_shape,\n            scale,\n            self.random_generator,\n        )\n        points = fgeometric.order_points(points)\n\n        matrix, max_width, max_height = fgeometric.compute_perspective_params(\n            points,\n            image_shape,\n        )\n\n        if self.fit_output:\n            matrix, max_width, max_height = fgeometric.expand_transform(\n                matrix,\n                image_shape,\n            )\n\n        return {\n            \"matrix\": matrix,\n            \"max_height\": max_height,\n            \"max_width\": max_width,\n            \"matrix_bbox\": matrix,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"keep_size\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"fit_output\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.PiecewiseAffine","title":"class PiecewiseAffine (scale=(0.03, 0.05), nb_rows=(4, 4), nb_cols=(4, 4), interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, absolute_scale=False, p=0.5, always_apply=None, keypoints_threshold=0.01) [view source on GitHub]","text":"

Apply piecewise affine transformations to the input image.

This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points around via affine transformations. This leads to local distortions in the image.

Parameters:

Name Type Description scale tuple[float, float] | float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Recommended values are in the range (0.01, 0.05) for small distortions, and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).

nb_rows tuple[int, int] | int

Number of rows of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of rows. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

nb_cols tuple[int, int] | int

Number of columns of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of columns. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

absolute_scale bool

If set to True, the value of the scale parameter will be treated as an absolute pixel value. If set to False, it will be treated as a fraction of the image height and width. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This augmentation is very slow. Consider using ElasticTransform instead, which is at least 10x faster.
  • The augmentation may not always produce visible effects, especially with small scale values.
  • For keypoints and bounding boxes, the transformation might move them outside the image boundaries. In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PiecewiseAffine(BaseDistortion):\n    \"\"\"Apply piecewise affine transformations to the input image.\n\n    This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points\n    around via affine transformations. This leads to local distortions in the image.\n\n    Args:\n        scale (tuple[float, float] | float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Recommended values are in the range (0.01, 0.05) for small distortions,\n            and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).\n        nb_rows (tuple[int, int] | int): Number of rows of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of rows.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        nb_cols (tuple[int, int] | int): Number of columns of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of columns.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        absolute_scale (bool): If set to True, the value of the scale parameter will be treated as an absolute\n            pixel value. If set to False, it will be treated as a fraction of the image height and width.\n            Default: False.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This augmentation is very slow. Consider using `ElasticTransform` instead, which is at least 10x faster.\n        - The augmentation may not always produce visible effects, especially with small scale values.\n        - For keypoints and bounding boxes, the transformation might move them outside the image boundaries.\n          In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed[\"image\"]\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        nb_rows: ScaleIntType\n        nb_cols: ScaleIntType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        cval: int | None = Field(deprecated=\"Deprecated. Does not have any effect.\")\n        cval_mask: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = Field(\n            deprecated=\"Deprecated. Does not have any effects.\",\n        )\n\n        absolute_scale: bool\n        keypoints_threshold: float = Field(\n            deprecated=\"This parameter is not used anymore\",\n        )\n\n        @field_validator(\"nb_rows\", \"nb_cols\")\n        @classmethod\n        def process_range(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 2, BIG_INTEGER\n            result = to_tuple(value, value)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.03, 0.05),\n        nb_rows: ScaleIntType = (4, 4),\n        nb_cols: ScaleIntType = (4, 4),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: int | None = None,\n        cval_mask: int | None = None,\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = None,\n        absolute_scale: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n        keypoints_threshold: float = 0.01,\n    ):\n        super().__init__(\n            p=p,\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n        )\n\n        warn(\n            \"This augmenter is very slow. Try to use ``ElasticTransform`` instead, which is at least 10x faster.\",\n            stacklevel=2,\n        )\n\n        self.scale = cast(tuple[float, float], scale)\n        self.nb_rows = cast(tuple[int, int], nb_rows)\n        self.nb_cols = cast(tuple[int, int], nb_cols)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.absolute_scale = absolute_scale\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"nb_rows\",\n            \"nb_cols\",\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"absolute_scale\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        nb_rows = np.clip(self.py_random.randint(*self.nb_rows), 2, None)\n        nb_cols = np.clip(self.py_random.randint(*self.nb_cols), 2, None)\n        scale = self.py_random.uniform(*self.scale)\n\n        map_x, map_y = fgeometric.create_piecewise_affine_maps(\n            image_shape=image_shape,\n            grid=(nb_rows, nb_cols),\n            scale=scale,\n            absolute_scale=self.absolute_scale,\n            random_generator=self.random_generator,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.RandomGridShuffle","title":"class RandomGridShuffle (grid=(3, 3), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shuffles the grid's cells on an image, mask, or keypoints, effectively rearranging patches within the image. This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.

Parameters:

Name Type Description grid tuple[int, int]

Size of the grid for splitting the image into cells. Each cell is shuffled randomly. For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled. Default: (3, 3)

p float

Probability that the transform will be applied. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This transform maintains consistency across all targets. If applied to an image and its corresponding mask or keypoints, the same shuffling will be applied to all.
  • The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2)) for the transform to have any effect.
  • Keypoints are moved along with their corresponding grid cell.
  • This transform could be useful when only micro features are important for the model, and memorizing the global structure could be harmful. For example:
  • Identifying the type of cell phone used to take a picture based on micro artifacts generated by phone post-processing algorithms, rather than the semantic features of the photo. See more at https://ieeexplore.ieee.org/abstract/document/8622031
  • Identifying stress, glucose, hydration levels based on skin images.

Mathematical Formulation: 1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter. 2. A random permutation P of integers from 0 to (mn - 1) is generated. 3. Each cell in the grid is assigned a number from 0 to (mn - 1) in row-major order. 4. The cells are then rearranged according to the permutation P.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4]\n... ])\n>>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image might look like this (one possible outcome):\n# [[4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1]]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class RandomGridShuffle(DualTransform):\n    \"\"\"Randomly shuffles the grid's cells on an image, mask, or keypoints,\n    effectively rearranging patches within the image.\n    This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.\n\n    Args:\n        grid (tuple[int, int]): Size of the grid for splitting the image into cells. Each cell is shuffled randomly.\n            For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled.\n            Default: (3, 3)\n        p (float): Probability that the transform will be applied. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform maintains consistency across all targets. If applied to an image and its corresponding\n          mask or keypoints, the same shuffling will be applied to all.\n        - The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2))\n          for the transform to have any effect.\n        - Keypoints are moved along with their corresponding grid cell.\n        - This transform could be useful when only micro features are important for the model, and memorizing\n          the global structure could be harmful. For example:\n          - Identifying the type of cell phone used to take a picture based on micro artifacts generated by\n            phone post-processing algorithms, rather than the semantic features of the photo.\n            See more at https://ieeexplore.ieee.org/abstract/document/8622031\n          - Identifying stress, glucose, hydration levels based on skin images.\n\n    Mathematical Formulation:\n        1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter.\n        2. A random permutation P of integers from 0 to (m*n - 1) is generated.\n        3. Each cell in the grid is assigned a number from 0 to (m*n - 1) in row-major order.\n        4. The cells are then rearranged according to the permutation P.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4]\n        ... ])\n        >>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image might look like this (one possible outcome):\n        # [[4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1]]\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        grid: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        grid: tuple[int, int] = (3, 3),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.grid = grid\n\n    def apply(\n        self,\n        img: np.ndarray,\n        tiles: np.ndarray,\n        mapping: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_image(img, tiles, mapping)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n        bboxes_returned = fgeometric.bboxes_grid_shuffle(\n            bboxes_denorm,\n            tiles,\n            mapping,\n            image_shape,\n            min_area=processor.params.min_area,\n            min_visibility=processor.params.min_visibility,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_keypoints(keypoints, tiles, mapping)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        original_tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.grid,\n            self.random_generator,\n        )\n        shape_groups = fgeometric.create_shape_groups(original_tiles)\n        mapping = fgeometric.shuffle_tiles_within_shape_groups(\n            shape_groups,\n            self.random_generator,\n        )\n\n        return {\"tiles\": original_tiles, \"mapping\": mapping}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"grid\",)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ShiftScaleRotate","title":"class ShiftScaleRotate (shift_limit=(-0.0625, 0.0625), scale_limit=(-0.1, 0.1), rotate_limit=(-45, 45), interpolation=1, border_mode=4, value=None, mask_value=None, shift_limit_x=None, shift_limit_y=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly apply affine transforms: translate, scale and rotate the input.

Parameters:

Name Type Description shift_limit float, float) or float

shift factor range for both height and width. If shift_limit is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).

scale_limit float, float) or float

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

rotate_limit int, int) or int

rotation range. If rotate_limit is a single int value, the range will be (-rotate_limit, rotate_limit). Default: (-45, 45).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

shift_limit_x float, float) or float

shift factor range for width. If it is set then this value instead of shift_limit will be used for shifting width. If shift_limit_x is a single float value, the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in the range [-1, 1]. Default: None.

shift_limit_y float, float) or float

shift factor range for height. If it is set then this value instead of shift_limit will be used for shifting height. If shift_limit_y is a single float value, the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie in the range [-, 1]. Default: None.

rotate_method str

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\". Default: \"largest_box\"

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ShiftScaleRotate(Affine):\n    \"\"\"Randomly apply affine transforms: translate, scale and rotate the input.\n\n    Args:\n        shift_limit ((float, float) or float): shift factor range for both height and width. If shift_limit\n            is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and\n            upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).\n        scale_limit ((float, float) or float): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        rotate_limit ((int, int) or int): rotation range. If rotate_limit is a single int value, the\n            range will be (-rotate_limit, rotate_limit). Default: (-45, 45).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        shift_limit_x ((float, float) or float): shift factor range for width. If it is set then this value\n            instead of shift_limit will be used for shifting width.  If shift_limit_x is a single float value,\n            the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in\n            the range [-1, 1]. Default: None.\n        shift_limit_y ((float, float) or float): shift factor range for height. If it is set then this value\n            instead of shift_limit will be used for shifting height.  If shift_limit_y is a single float value,\n            the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie\n            in the range [-, 1]. Default: None.\n        rotate_method (str): rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\".\n            Default: \"largest_box\"\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        shift_limit: SymmetricRangeType = (-0.0625, 0.0625)\n        scale_limit: SymmetricRangeType = (-0.1, 0.1)\n        rotate_limit: SymmetricRangeType = (-45, 45)\n        interpolation: InterpolationType = cv2.INTER_LINEAR\n        border_mode: BorderModeType = cv2.BORDER_REFLECT_101\n\n        value: ColorType | None = Field(\n            default=None,\n            deprecated=\"Deprecated. Use fill instead.\",\n        )\n        mask_value: ColorType | None = Field(\n            default=None,\n            deprecated=\"Deprecated. Use fill_mask instead.\",\n        )\n\n        fill: ColorType = 0\n        fill_mask: ColorType = 0\n\n        shift_limit_x: ScaleFloatType | None = Field(default=None)\n        shift_limit_y: ScaleFloatType | None = Field(default=None)\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\"\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def check_shift_limit(self) -> Self:\n            bounds = -1, 1\n            self.shift_limit_x = to_tuple(\n                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_x, *bounds, \"shift_limit_x\")\n            self.shift_limit_y = to_tuple(\n                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_y, *bounds, \"shift_limit_y\")\n\n            return self\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> ScaleFloatType:\n            bounds = 0, float(\"inf\")\n            result = to_tuple(value, bias=1.0)\n            check_range(result, *bounds, str(info.field_name))\n            return result\n\n    def __init__(\n        self,\n        shift_limit: ScaleFloatType = (-0.0625, 0.0625),\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        rotate_limit: ScaleFloatType = (-45, 45),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        shift_limit_x: ScaleFloatType | None = None,\n        shift_limit_y: ScaleFloatType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: InterpolationType = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        shift_limit_x = cast(tuple[float, float], shift_limit_x)\n        shift_limit_y = cast(tuple[float, float], shift_limit_y)\n        super().__init__(\n            scale=scale_limit,\n            translate_percent={\"x\": shift_limit_x, \"y\": shift_limit_y},\n            rotate=rotate_limit,\n            shear=(0, 0),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            fit_output=False,\n            keep_ratio=False,\n            rotate_method=rotate_method,\n            always_apply=always_apply,\n            p=p,\n        )\n        warn(\n            \"ShiftScaleRotate is deprecated. Please use Affine transform instead.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        self.shift_limit_x = shift_limit_x\n        self.shift_limit_y = shift_limit_y\n\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"shift_limit_x\": self.shift_limit_x,\n            \"shift_limit_y\": self.shift_limit_y,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n            \"rotate_limit\": self.rotate_limit,\n            \"interpolation\": self.interpolation,\n            \"border_mode\": self.border_mode,\n            \"fill\": self.fill,\n            \"fill_mask\": self.fill_mask,\n            \"rotate_method\": self.rotate_method,\n            \"mask_interpolation\": self.mask_interpolation,\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ThinPlateSpline","title":"class ThinPlateSpline (scale_range=(0.2, 0.4), num_control_points=4, interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.

Imagine the image printed on a thin metal plate that can be bent and warped smoothly: - Control points act like pins pushing or pulling the plate - The plate resists sharp bending, creating smooth deformations - The transformation maintains continuity (no tears or folds) - Areas between control points are interpolated naturally

The transform works by: 1. Creating a regular grid of control points (like pins in the plate) 2. Randomly displacing these points (like pushing/pulling the pins) 3. Computing a smooth interpolation (like the plate bending) 4. Applying the resulting deformation to the image

Parameters:

Name Type Description scale_range tuple[float, float]

Range for random displacement of control points. Values should be in [0.0, 1.0]: - 0.0: No displacement (identity transform) - 0.1: Subtle warping - 0.2-0.4: Moderate deformation (recommended range) - 0.5+: Strong warping Default: (0.2, 0.4)

num_control_points int

Number of control points per side. Creates a grid of num_control_points x num_control_points points. - 2: Minimal deformation (affine-like) - 3-4: Moderate flexibility (recommended) - 5+: More local deformation control Must be >= 2. Default: 4

interpolation int

OpenCV interpolation flag. Used for image sampling. See also: cv2.INTER_* Default: cv2.INTER_LINEAR

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • The transformation preserves smoothness and continuity
  • Stronger scale values may create more extreme deformations
  • Higher number of control points allows more local deformations
  • The same deformation is applied consistently to all targets

Examples:

Python
>>> import albumentations as A\n>>> # Basic usage\n>>> transform = A.ThinPlateSpline()\n>>>\n>>> # Subtle deformation\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.1, 0.2),\n...     num_control_points=3\n... )\n>>>\n>>> # Strong warping with fine control\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.3, 0.5),\n...     num_control_points=5,\n... )\n

References

  • \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\" by F.L. Bookstein https://doi.org/10.1109/34.24792

  • Thin Plate Splines in Computer Vision: https://en.wikipedia.org/wiki/Thin_plate_spline

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline

See Also: - ElasticTransform: For different type of non-rigid deformation - GridDistortion: For grid-based warping - OpticalDistortion: For lens-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ThinPlateSpline(BaseDistortion):\n    r\"\"\"Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.\n\n    Imagine the image printed on a thin metal plate that can be bent and warped smoothly:\n    - Control points act like pins pushing or pulling the plate\n    - The plate resists sharp bending, creating smooth deformations\n    - The transformation maintains continuity (no tears or folds)\n    - Areas between control points are interpolated naturally\n\n    The transform works by:\n    1. Creating a regular grid of control points (like pins in the plate)\n    2. Randomly displacing these points (like pushing/pulling the pins)\n    3. Computing a smooth interpolation (like the plate bending)\n    4. Applying the resulting deformation to the image\n\n\n    Args:\n        scale_range (tuple[float, float]): Range for random displacement of control points.\n            Values should be in [0.0, 1.0]:\n            - 0.0: No displacement (identity transform)\n            - 0.1: Subtle warping\n            - 0.2-0.4: Moderate deformation (recommended range)\n            - 0.5+: Strong warping\n            Default: (0.2, 0.4)\n\n        num_control_points (int): Number of control points per side.\n            Creates a grid of num_control_points x num_control_points points.\n            - 2: Minimal deformation (affine-like)\n            - 3-4: Moderate flexibility (recommended)\n            - 5+: More local deformation control\n            Must be >= 2. Default: 4\n\n        interpolation (int): OpenCV interpolation flag. Used for image sampling.\n            See also: cv2.INTER_*\n            Default: cv2.INTER_LINEAR\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transformation preserves smoothness and continuity\n        - Stronger scale values may create more extreme deformations\n        - Higher number of control points allows more local deformations\n        - The same deformation is applied consistently to all targets\n\n    Example:\n        >>> import albumentations as A\n        >>> # Basic usage\n        >>> transform = A.ThinPlateSpline()\n        >>>\n        >>> # Subtle deformation\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.1, 0.2),\n        ...     num_control_points=3\n        ... )\n        >>>\n        >>> # Strong warping with fine control\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.3, 0.5),\n        ...     num_control_points=5,\n        ... )\n\n    References:\n        - \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\"\n          by F.L. Bookstein\n          https://doi.org/10.1109/34.24792\n\n        - Thin Plate Splines in Computer Vision:\n          https://en.wikipedia.org/wiki/Thin_plate_spline\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline\n\n    See Also:\n        - ElasticTransform: For different type of non-rigid deformation\n        - GridDistortion: For grid-based warping\n        - OpticalDistortion: For lens-like distortions\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        scale_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        num_control_points: int = Field(ge=2)\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.2, 0.4),\n        num_control_points: int = 4,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale_range = scale_range\n        self.num_control_points = num_control_points\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        # Create regular grid of control points\n        grid_size = self.num_control_points\n        x = np.linspace(0, 1, grid_size)\n        y = np.linspace(0, 1, grid_size)\n        src_points = np.stack(np.meshgrid(x, y), axis=-1).reshape(-1, 2)\n\n        # Add random displacement to destination points\n        scale = self.py_random.uniform(*self.scale_range) / 10\n        dst_points = src_points + self.random_generator.normal(\n            0,\n            scale,\n            src_points.shape,\n        )\n\n        # Compute TPS weights\n        weights, affine = fgeometric.compute_tps_weights(src_points, dst_points)\n\n        # Create grid of points\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        points = np.stack([x.flatten(), y.flatten()], axis=1).astype(np.float32)\n\n        # Transform points\n        transformed = fgeometric.tps_transform(\n            points / [width, height],\n            src_points,\n            weights,\n            affine,\n        )\n        transformed *= [width, height]\n\n        return {\n            \"map_x\": transformed[:, 0].reshape(height, width).astype(np.float32),\n            \"map_y\": transformed[:, 1].reshape(height, width).astype(np.float32),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale_range\",\n            \"num_control_points\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Transpose","title":"class Transpose [view source on GitHub]","text":"

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.Transpose(p=1.0)\n>>> result = transform(image=image)\n>>> transposed_image = result['image']\n>>> print(transposed_image)\n[[[ 1  2  3]\n  [ 7  8  9]]\n [[ 4  5  6]\n  [10 11 12]]]\n# The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Transpose(DualTransform):\n    \"\"\"Transpose the input by swapping its rows and columns.\n\n    This transform flips the image over its main diagonal, effectively switching its width and height.\n    It's equivalent to a 90-degree rotation followed by a horizontal flip.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The dimensions of the output will be swapped compared to the input. For example,\n          an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).\n        - This transform is its own inverse. Applying it twice will return the original input.\n        - For multi-channel images (like RGB), the channels are preserved in their original order.\n        - Bounding boxes will have their coordinates adjusted to match the new image dimensions.\n        - Keypoints will have their x and y coordinates swapped.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (y_min, x_min, y_max, x_max)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (y, x)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.Transpose(p=1.0)\n        >>> result = transform(image=image)\n        >>> transposed_image = result['image']\n        >>> print(transposed_image)\n        [[[ 1  2  3]\n          [ 7  8  9]]\n         [[ 4  5  6]\n          [10 11 12]]]\n        # The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.transpose(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_transpose(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_transpose(keypoints)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.VerticalFlip","title":"class VerticalFlip [view source on GitHub]","text":"

Flip the input vertically around the x-axis.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.VerticalFlip(p=1.0)\n>>> result = transform(image=image)\n>>> flipped_image = result['image']\n>>> print(flipped_image)\n[[[ 7  8  9]\n  [10 11 12]]\n [[ 1  2  3]\n  [ 4  5  6]]]\n# The original image is flipped vertically, with rows reversed\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class VerticalFlip(DualTransform):\n    \"\"\"Flip the input vertically around the x-axis.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform flips the image upside down. The top of the image becomes the bottom and vice versa.\n        - The dimensions of the image remain unchanged.\n        - For multi-channel images (like RGB), each channel is flipped independently.\n        - Bounding boxes are adjusted to match their new positions in the flipped image.\n        - Keypoints are moved to their new positions in the flipped image.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (x_min, H-y_max, x_max, H-y_min)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (x, H-y)\n        where H is the height of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.VerticalFlip(p=1.0)\n        >>> result = transform(image=image)\n        >>> flipped_image = result['image']\n        >>> print(flipped_image)\n        [[[ 7  8  9]\n          [10 11 12]]\n         [[ 1  2  3]\n          [ 4  5  6]]]\n        # The original image is flipped vertically, with rows reversed\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return vflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_vflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_vflip(keypoints, params[\"shape\"][0])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/","title":"Transforms (augmentations.transforms)","text":""},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.AdditiveNoise","title":"class AdditiveNoise (noise_type='uniform', spatial_mode='constant', noise_params=None, approximation=1.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random noise to image channels using various noise distributions.

This transform generates noise using different probability distributions and applies it to image channels. The noise can be generated in three spatial modes and supports multiple noise distributions, each with configurable parameters.

Parameters:

Name Type Description noise_type Literal['uniform', 'gaussian', 'laplace', 'beta']

Type of noise distribution to use. Options: - \"uniform\": Uniform distribution, good for simple random perturbations - \"gaussian\": Normal distribution, models natural random processes - \"laplace\": Similar to Gaussian but with heavier tails, good for outliers - \"beta\": Flexible bounded distribution, can be symmetric or skewed

spatial_mode Literal['constant', 'per_pixel', 'shared']

How to generate and apply the noise. Options: - \"constant\": One noise value per channel, fastest - \"per_pixel\": Independent noise value for each pixel and channel, slowest - \"shared\": One noise map shared across all channels, medium speed

approximation float

float in [0, 1], default=1.0 Controls noise generation speed vs quality tradeoff. - 1.0: Generate full resolution noise (slowest, highest quality) - 0.5: Generate noise at half resolution and upsample - 0.25: Generate noise at quarter resolution and upsample Only affects 'per_pixel' and 'shared' spatial modes.

noise_params dict[str, Any] | None

Parameters for the chosen noise distribution. Must match the noise_type:

uniform: ranges: list[tuple[float, float]] List of (min, max) ranges for each channel. Each range must be in [-1, 1]. If only one range is provided, it will be used for all channels.

    [(-0.2, 0.2)]  # Same range for all channels\n    [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]  # Different ranges for RGB\n

gaussian: mean_range: tuple[float, float], default (0.0, 0.0) Range for sampling mean value, in [-1, 1] std_range: tuple[float, float], default (0.1, 0.1) Range for sampling standard deviation, in [0, 1]

laplace: mean_range: tuple[float, float], default (0.0, 0.0) Range for sampling location parameter, in [-1, 1] scale_range: tuple[float, float], default (0.1, 0.1) Range for sampling scale parameter, in [0, 1]

beta: alpha_range: tuple[float, float], default (0.5, 1.5) Value < 1 = U-shaped, Value > 1 = Bell-shaped Range for sampling first shape parameter, in (0, inf) beta_range: tuple[float, float], default (0.5, 1.5) Value < 1 = U-shaped, Value > 1 = Bell-shaped Range for sampling second shape parameter, in (0, inf) scale_range: tuple[float, float], default (0.1, 0.3) Smaller scale for subtler noise Range for sampling output scale, in [0, 1]

Note

Performance considerations: - \"constant\" mode is fastest as it generates only C values (C = number of channels) - \"shared\" mode generates HxW values and reuses them for all channels - \"per_pixel\" mode generates HxWxC values, slowest but most flexible

Distribution characteristics: - uniform: Equal probability within range, good for simple perturbations - gaussian: Bell-shaped, symmetric, good for natural noise - laplace: Like gaussian but with heavier tails, good for outliers - beta: Very flexible shape, can be uniform, bell-shaped, or U-shaped

Implementation details: - All noise is generated in normalized range and scaled by image max value - For uint8 images, final noise range is [-255, 255] - For float images, final noise range is [-1, 1]

Examples:

Constant RGB shift with different ranges per channel:

Python
>>> transform = AdditiveNoise(\n...     noise_type=\"uniform\",\n...     spatial_mode=\"constant\",\n...     noise_params={\"ranges\": [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]}\n... )\n

Gaussian noise shared across channels:

Python
>>> transform = AdditiveNoise(\n...     noise_type=\"gaussian\",\n...     spatial_mode=\"shared\",\n...     noise_params={\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)}\n... )\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class AdditiveNoise(ImageOnlyTransform):\n    \"\"\"Apply random noise to image channels using various noise distributions.\n\n    This transform generates noise using different probability distributions and applies it\n    to image channels. The noise can be generated in three spatial modes and supports\n    multiple noise distributions, each with configurable parameters.\n\n    Args:\n        noise_type: Type of noise distribution to use. Options:\n            - \"uniform\": Uniform distribution, good for simple random perturbations\n            - \"gaussian\": Normal distribution, models natural random processes\n            - \"laplace\": Similar to Gaussian but with heavier tails, good for outliers\n            - \"beta\": Flexible bounded distribution, can be symmetric or skewed\n\n        spatial_mode: How to generate and apply the noise. Options:\n            - \"constant\": One noise value per channel, fastest\n            - \"per_pixel\": Independent noise value for each pixel and channel, slowest\n            - \"shared\": One noise map shared across all channels, medium speed\n\n        approximation: float in [0, 1], default=1.0\n            Controls noise generation speed vs quality tradeoff.\n            - 1.0: Generate full resolution noise (slowest, highest quality)\n            - 0.5: Generate noise at half resolution and upsample\n            - 0.25: Generate noise at quarter resolution and upsample\n            Only affects 'per_pixel' and 'shared' spatial modes.\n\n        noise_params: Parameters for the chosen noise distribution.\n            Must match the noise_type:\n\n            uniform:\n                ranges: list[tuple[float, float]]\n                    List of (min, max) ranges for each channel.\n                    Each range must be in [-1, 1].\n                    If only one range is provided, it will be used for all channels.\n\n                    [(-0.2, 0.2)]  # Same range for all channels\n                    [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]  # Different ranges for RGB\n\n            gaussian:\n                mean_range: tuple[float, float], default (0.0, 0.0)\n                    Range for sampling mean value, in [-1, 1]\n                std_range: tuple[float, float], default (0.1, 0.1)\n                    Range for sampling standard deviation, in [0, 1]\n\n            laplace:\n                mean_range: tuple[float, float], default (0.0, 0.0)\n                    Range for sampling location parameter, in [-1, 1]\n                scale_range: tuple[float, float], default (0.1, 0.1)\n                    Range for sampling scale parameter, in [0, 1]\n\n            beta:\n                alpha_range: tuple[float, float], default (0.5, 1.5)\n                    Value < 1 = U-shaped, Value > 1 = Bell-shaped\n                    Range for sampling first shape parameter, in (0, inf)\n                beta_range: tuple[float, float], default (0.5, 1.5)\n                    Value < 1 = U-shaped, Value > 1 = Bell-shaped\n                    Range for sampling second shape parameter, in (0, inf)\n                scale_range: tuple[float, float], default (0.1, 0.3)\n                    Smaller scale for subtler noise\n                    Range for sampling output scale, in [0, 1]\n\n    Note:\n        Performance considerations:\n            - \"constant\" mode is fastest as it generates only C values (C = number of channels)\n            - \"shared\" mode generates HxW values and reuses them for all channels\n            - \"per_pixel\" mode generates HxWxC values, slowest but most flexible\n\n        Distribution characteristics:\n            - uniform: Equal probability within range, good for simple perturbations\n            - gaussian: Bell-shaped, symmetric, good for natural noise\n            - laplace: Like gaussian but with heavier tails, good for outliers\n            - beta: Very flexible shape, can be uniform, bell-shaped, or U-shaped\n\n        Implementation details:\n            - All noise is generated in normalized range and scaled by image max value\n            - For uint8 images, final noise range is [-255, 255]\n            - For float images, final noise range is [-1, 1]\n\n    Examples:\n        Constant RGB shift with different ranges per channel:\n        >>> transform = AdditiveNoise(\n        ...     noise_type=\"uniform\",\n        ...     spatial_mode=\"constant\",\n        ...     noise_params={\"ranges\": [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]}\n        ... )\n\n        Gaussian noise shared across channels:\n        >>> transform = AdditiveNoise(\n        ...     noise_type=\"gaussian\",\n        ...     spatial_mode=\"shared\",\n        ...     noise_params={\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)}\n        ... )\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"]\n        spatial_mode: Literal[\"constant\", \"per_pixel\", \"shared\"]\n        noise_params: dict[str, Any] | None\n        approximation: float = Field(ge=0.0, le=1.0)\n\n        @model_validator(mode=\"after\")\n        def validate_noise_params(self) -> Self:\n            # Default parameters for each noise type\n            default_params = {\n                \"uniform\": {\n                    \"ranges\": [(-0.1, 0.1)],  # Single channel by default\n                },\n                \"gaussian\": {\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)},\n                \"laplace\": {\"mean_range\": (0.0, 0.0), \"scale_range\": (0.05, 0.15)},\n                \"beta\": {\n                    \"alpha_range\": (0.5, 1.5),\n                    \"beta_range\": (0.5, 1.5),\n                    \"scale_range\": (0.1, 0.3),\n                },\n            }\n\n            # Use default params if none provided\n            params_dict = self.noise_params if self.noise_params is not None else default_params[self.noise_type]\n\n            # Convert dict to appropriate NoiseParams object\n            params_class = {\n                \"uniform\": UniformParams,\n                \"gaussian\": GaussianParams,\n                \"laplace\": LaplaceParams,\n                \"beta\": BetaParams,\n            }[self.noise_type]\n\n            # Add noise_type to params if not present\n            params_dict = {**params_dict, \"noise_type\": self.noise_type}  # type: ignore[dict-item]\n            self.noise_params = params_class(**params_dict)\n\n            return self\n\n    def __init__(\n        self,\n        noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"] = \"uniform\",\n        spatial_mode: Literal[\"constant\", \"per_pixel\", \"shared\"] = \"constant\",\n        noise_params: dict[str, Any] | None = None,\n        approximation: float = 1.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.noise_type = noise_type\n        self.spatial_mode = spatial_mode\n        self.noise_params = noise_params\n        self.approximation = approximation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        noise_map: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_noise(img, noise_map)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n\n        noise_map = fmain.generate_noise(\n            noise_type=self.noise_type,\n            spatial_mode=self.spatial_mode,\n            shape=image.shape,\n            params=self.noise_params,\n            max_value=max_value,\n            approximation=self.approximation,\n            random_generator=self.random_generator,\n        )\n        return {\"noise_map\": noise_map}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"noise_type\", \"spatial_mode\", \"noise_params\", \"approximation\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.AutoContrast","title":"class AutoContrast (p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random auto contrast to images.

Auto contrast enhances image contrast by stretching the intensity range to use the full range while preserving relative intensities. For each color channel: 1. Compute histogram 2. Find cumulative percentiles 3. Clip and scale intensities to full range

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class AutoContrast(ImageOnlyTransform):\n    \"\"\"Apply random auto contrast to images.\n\n    Auto contrast enhances image contrast by stretching the intensity range\n    to use the full range while preserving relative intensities. For each\n    color channel:\n    1. Compute histogram\n    2. Find cumulative percentiles\n    3. Clip and scale intensities to full range\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.auto_contrast(img)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.BetaParams","title":"class BetaParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class BetaParams(NoiseParamsBase):\n    noise_type: Literal[\"beta\"] = \"beta\"\n    alpha_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0)),\n    ]\n    beta_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0)),\n    ]\n    scale_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.CLAHE","title":"class CLAHE (clip_limit=4.0, tile_grid_size=(8, 8), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.

CLAHE is an advanced method of improving the contrast in an image. Unlike regular histogram equalization, which operates on the entire image, CLAHE operates on small regions (tiles) in the image. This results in a more balanced equalization, preventing over-amplification of contrast in areas with initially low contrast.

Parameters:

Name Type Description clip_limit tuple[float, float] | float

Controls the contrast enhancement limit. - If a single float is provided, the range will be (1, clip_limit). - If a tuple of two floats is provided, it defines the range for random selection. Higher values allow for more contrast enhancement, but may also increase noise. Default: (1, 4)

tile_grid_size tuple[int, int]

Defines the number of tiles in the row and column directions. Format is (rows, columns). Smaller tile sizes can lead to more localized enhancements, while larger sizes give results closer to global histogram equalization. Default: (8, 8)

p float

Probability of applying the transform. Default: 0.5

Notes

  • Supports only RGB or grayscale images.
  • For color images, CLAHE is applied to the L channel in the LAB color space.
  • The clip limit determines the maximum slope of the cumulative histogram. A lower clip limit will result in more contrast limiting.
  • Tile grid size affects the adaptiveness of the method. More tiles increase local adaptiveness but can lead to an unnatural look if set too high.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1, 3

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.CLAHE(clip_limit=(1, 4), tile_grid_size=(8, 8), p=1.0)\n>>> result = transform(image=image)\n>>> clahe_image = result[\"image\"]\n

References

  • https://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html
  • Zuiderveld, Karel. \"Contrast Limited Adaptive Histogram Equalization.\" Graphic Gems IV. Academic Press Professional, Inc., 1994.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class CLAHE(ImageOnlyTransform):\n    \"\"\"Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.\n\n    CLAHE is an advanced method of improving the contrast in an image. Unlike regular histogram\n    equalization, which operates on the entire image, CLAHE operates on small regions (tiles)\n    in the image. This results in a more balanced equalization, preventing over-amplification\n    of contrast in areas with initially low contrast.\n\n    Args:\n        clip_limit (tuple[float, float] | float): Controls the contrast enhancement limit.\n            - If a single float is provided, the range will be (1, clip_limit).\n            - If a tuple of two floats is provided, it defines the range for random selection.\n            Higher values allow for more contrast enhancement, but may also increase noise.\n            Default: (1, 4)\n\n        tile_grid_size (tuple[int, int]): Defines the number of tiles in the row and column directions.\n            Format is (rows, columns). Smaller tile sizes can lead to more localized enhancements,\n            while larger sizes give results closer to global histogram equalization.\n            Default: (8, 8)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - Supports only RGB or grayscale images.\n        - For color images, CLAHE is applied to the L channel in the LAB color space.\n        - The clip limit determines the maximum slope of the cumulative histogram. A lower\n          clip limit will result in more contrast limiting.\n        - Tile grid size affects the adaptiveness of the method. More tiles increase local\n          adaptiveness but can lead to an unnatural look if set too high.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1, 3\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.CLAHE(clip_limit=(1, 4), tile_grid_size=(8, 8), p=1.0)\n        >>> result = transform(image=image)\n        >>> clahe_image = result[\"image\"]\n\n    References:\n        - https://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html\n        - Zuiderveld, Karel. \"Contrast Limited Adaptive Histogram Equalization.\"\n          Graphic Gems IV. Academic Press Professional, Inc., 1994.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        clip_limit: OnePlusFloatRangeType\n        tile_grid_size: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    def __init__(\n        self,\n        clip_limit: ScaleFloatType = 4.0,\n        tile_grid_size: tuple[int, int] = (8, 8),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.clip_limit = cast(tuple[float, float], clip_limit)\n        self.tile_grid_size = tile_grid_size\n\n    def apply(self, img: np.ndarray, clip_limit: float, **params: Any) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"CLAHE transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n\n        return fmain.clahe(img, clip_limit, self.tile_grid_size)\n\n    def get_params(self) -> dict[str, float]:\n        return {\"clip_limit\": self.py_random.uniform(*self.clip_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"clip_limit\", \"tile_grid_size\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ChannelShuffle","title":"class ChannelShuffle [view source on GitHub]","text":"

Randomly rearrange channels of the image.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ChannelShuffle(ImageOnlyTransform):\n    \"\"\"Randomly rearrange channels of the image.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    def apply(\n        self,\n        img: np.ndarray,\n        channels_shuffled: tuple[int, ...],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.channel_shuffle(img, channels_shuffled)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        ch_arr = list(range(params[\"shape\"][2]))\n        self.random_generator.shuffle(ch_arr)\n        return {\"channels_shuffled\": ch_arr}\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ChromaticAberration","title":"class ChromaticAberration (primary_distortion_limit=(-0.02, 0.02), secondary_distortion_limit=(-0.05, 0.05), mode='green_purple', interpolation=1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Add lateral chromatic aberration by distorting the red and blue channels of the input image.

Chromatic aberration is an optical effect that occurs when a lens fails to focus all colors to the same point. This transform simulates this effect by applying different radial distortions to the red and blue channels of the image, while leaving the green channel unchanged.

Parameters:

Name Type Description primary_distortion_limit tuple[float, float] | float

Range of the primary radial distortion coefficient. If a single float value is provided, the range will be (-primary_distortion_limit, primary_distortion_limit). This parameter controls the distortion in the center of the image: - Positive values result in pincushion distortion (edges bend inward) - Negative values result in barrel distortion (edges bend outward) Default: (-0.02, 0.02).

secondary_distortion_limit tuple[float, float] | float

Range of the secondary radial distortion coefficient. If a single float value is provided, the range will be (-secondary_distortion_limit, secondary_distortion_limit). This parameter controls the distortion in the corners of the image: - Positive values enhance pincushion distortion - Negative values enhance barrel distortion Default: (-0.05, 0.05).

mode Literal[\"green_purple\", \"red_blue\", \"random\"]

Type of color fringing to apply. Options are: - 'green_purple': Distorts red and blue channels in opposite directions, creating green-purple fringing. - 'red_blue': Distorts red and blue channels in the same direction, creating red-blue fringing. - 'random': Randomly chooses between 'green_purple' and 'red_blue' modes for each application. Default: 'green_purple'.

interpolation InterpolationType

Flag specifying the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • This transform only affects RGB images. Grayscale images will raise an error.
  • The strength of the effect depends on both primary and secondary distortion limits.
  • Higher absolute values for distortion limits will result in more pronounced chromatic aberration.
  • The 'green_purple' mode tends to produce more noticeable effects than 'red_blue'.

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> transform = A.ChromaticAberration(\n...     primary_distortion_limit=0.05,\n...     secondary_distortion_limit=0.1,\n...     mode='green_purple',\n...     interpolation=cv2.INTER_LINEAR,\n...     p=1.0\n... )\n>>> transformed = transform(image=image)\n>>> aberrated_image = transformed['image']\n

References

  • https://en.wikipedia.org/wiki/Chromatic_aberration
  • https://www.researchgate.net/publication/320691320_Chromatic_Aberration_in_Digital_Images

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ChromaticAberration(ImageOnlyTransform):\n    \"\"\"Add lateral chromatic aberration by distorting the red and blue channels of the input image.\n\n    Chromatic aberration is an optical effect that occurs when a lens fails to focus all colors to the same point.\n    This transform simulates this effect by applying different radial distortions to the red and blue channels\n    of the image, while leaving the green channel unchanged.\n\n    Args:\n        primary_distortion_limit (tuple[float, float] | float): Range of the primary radial distortion coefficient.\n            If a single float value is provided, the range\n            will be (-primary_distortion_limit, primary_distortion_limit).\n            This parameter controls the distortion in the center of the image:\n            - Positive values result in pincushion distortion (edges bend inward)\n            - Negative values result in barrel distortion (edges bend outward)\n            Default: (-0.02, 0.02).\n\n        secondary_distortion_limit (tuple[float, float] | float): Range of the secondary radial distortion coefficient.\n            If a single float value is provided, the range\n            will be (-secondary_distortion_limit, secondary_distortion_limit).\n            This parameter controls the distortion in the corners of the image:\n            - Positive values enhance pincushion distortion\n            - Negative values enhance barrel distortion\n            Default: (-0.05, 0.05).\n\n        mode (Literal[\"green_purple\", \"red_blue\", \"random\"]): Type of color fringing to apply. Options are:\n            - 'green_purple': Distorts red and blue channels in opposite directions, creating green-purple fringing.\n            - 'red_blue': Distorts red and blue channels in the same direction, creating red-blue fringing.\n            - 'random': Randomly chooses between 'green_purple' and 'red_blue' modes for each application.\n            Default: 'green_purple'.\n\n        interpolation (InterpolationType): Flag specifying the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - This transform only affects RGB images. Grayscale images will raise an error.\n        - The strength of the effect depends on both primary and secondary distortion limits.\n        - Higher absolute values for distortion limits will result in more pronounced chromatic aberration.\n        - The 'green_purple' mode tends to produce more noticeable effects than 'red_blue'.\n\n    Example:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> transform = A.ChromaticAberration(\n        ...     primary_distortion_limit=0.05,\n        ...     secondary_distortion_limit=0.1,\n        ...     mode='green_purple',\n        ...     interpolation=cv2.INTER_LINEAR,\n        ...     p=1.0\n        ... )\n        >>> transformed = transform(image=image)\n        >>> aberrated_image = transformed['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Chromatic_aberration\n        - https://www.researchgate.net/publication/320691320_Chromatic_Aberration_in_Digital_Images\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        primary_distortion_limit: SymmetricRangeType\n        secondary_distortion_limit: SymmetricRangeType\n        mode: ChromaticAberrationMode\n        interpolation: InterpolationType\n\n    def __init__(\n        self,\n        primary_distortion_limit: ScaleFloatType = (-0.02, 0.02),\n        secondary_distortion_limit: ScaleFloatType = (-0.05, 0.05),\n        mode: ChromaticAberrationMode = \"green_purple\",\n        interpolation: InterpolationType = cv2.INTER_LINEAR,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.primary_distortion_limit = cast(\n            tuple[float, float],\n            primary_distortion_limit,\n        )\n        self.secondary_distortion_limit = cast(\n            tuple[float, float],\n            secondary_distortion_limit,\n        )\n        self.mode = mode\n        self.interpolation = interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        primary_distortion_red: float,\n        secondary_distortion_red: float,\n        primary_distortion_blue: float,\n        secondary_distortion_blue: float,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.chromatic_aberration(\n            img,\n            primary_distortion_red,\n            secondary_distortion_red,\n            primary_distortion_blue,\n            secondary_distortion_blue,\n            self.interpolation,\n        )\n\n    def get_params(self) -> dict[str, float]:\n        primary_distortion_red = self.py_random.uniform(*self.primary_distortion_limit)\n        secondary_distortion_red = self.py_random.uniform(\n            *self.secondary_distortion_limit,\n        )\n        primary_distortion_blue = self.py_random.uniform(*self.primary_distortion_limit)\n        secondary_distortion_blue = self.py_random.uniform(\n            *self.secondary_distortion_limit,\n        )\n\n        secondary_distortion_red = self._match_sign(\n            primary_distortion_red,\n            secondary_distortion_red,\n        )\n        secondary_distortion_blue = self._match_sign(\n            primary_distortion_blue,\n            secondary_distortion_blue,\n        )\n\n        if self.mode == \"green_purple\":\n            # distortion coefficients of the red and blue channels have the same sign\n            primary_distortion_blue = self._match_sign(\n                primary_distortion_red,\n                primary_distortion_blue,\n            )\n            secondary_distortion_blue = self._match_sign(\n                secondary_distortion_red,\n                secondary_distortion_blue,\n            )\n        if self.mode == \"red_blue\":\n            # distortion coefficients of the red and blue channels have the opposite sign\n            primary_distortion_blue = self._unmatch_sign(\n                primary_distortion_red,\n                primary_distortion_blue,\n            )\n            secondary_distortion_blue = self._unmatch_sign(\n                secondary_distortion_red,\n                secondary_distortion_blue,\n            )\n\n        return {\n            \"primary_distortion_red\": primary_distortion_red,\n            \"secondary_distortion_red\": secondary_distortion_red,\n            \"primary_distortion_blue\": primary_distortion_blue,\n            \"secondary_distortion_blue\": secondary_distortion_blue,\n        }\n\n    @staticmethod\n    def _match_sign(a: float, b: float) -> float:\n        # Match the sign of b to a\n        if (a < 0 < b) or (a > 0 > b):\n            return -b\n        return b\n\n    @staticmethod\n    def _unmatch_sign(a: float, b: float) -> float:\n        # Unmatch the sign of b to a\n        if (a < 0 and b < 0) or (a > 0 and b > 0):\n            return -b\n        return b\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return (\n            \"primary_distortion_limit\",\n            \"secondary_distortion_limit\",\n            \"mode\",\n            \"interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ColorJitter","title":"class ColorJitter (brightness=(0.8, 1.2), contrast=(0.8, 1.2), saturation=(0.8, 1.2), hue=(-0.5, 0.5), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly changes the brightness, contrast, saturation, and hue of an image.

This transform is similar to torchvision's ColorJitter but with some differences due to the use of OpenCV instead of Pillow. The main differences are: 1. OpenCV and Pillow use different formulas to convert images to HSV format. 2. This implementation uses value saturation instead of uint8 overflow as in Pillow.

These differences may result in slightly different output compared to torchvision's ColorJitter.

Parameters:

Name Type Description brightness tuple[float, float] | float

How much to jitter brightness. If float: The brightness factor is chosen uniformly from [max(0, 1 - brightness), 1 + brightness]. If tuple: The brightness factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

contrast tuple[float, float] | float

How much to jitter contrast. If float: The contrast factor is chosen uniformly from [max(0, 1 - contrast), 1 + contrast]. If tuple: The contrast factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

saturation tuple[float, float] | float

How much to jitter saturation. If float: The saturation factor is chosen uniformly from [max(0, 1 - saturation), 1 + saturation]. If tuple: The saturation factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

hue float or tuple of float (min, max

How much to jitter hue. If float: The hue factor is chosen uniformly from [-hue, hue]. Should have 0 <= hue <= 0.5. If tuple: The hue factor is sampled from the range specified. Values should be in range [-0.5, 0.5]. Default: (-0.5, 0.5)

p (float): Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: 1, 3

Note

  • The order of application for these color transformations is random for each image.
  • The ranges for brightness, contrast, and saturation are applied as multiplicative factors.
  • The range for hue is applied as an additive factor.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> jittered_image = result['image']\n

References

  • https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.ColorJitter
  • https://docs.opencv.org/3.4/de/d25/imgproc_color_conversions.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ColorJitter(ImageOnlyTransform):\n    \"\"\"Randomly changes the brightness, contrast, saturation, and hue of an image.\n\n    This transform is similar to torchvision's ColorJitter but with some differences due to the use of OpenCV\n    instead of Pillow. The main differences are:\n    1. OpenCV and Pillow use different formulas to convert images to HSV format.\n    2. This implementation uses value saturation instead of uint8 overflow as in Pillow.\n\n    These differences may result in slightly different output compared to torchvision's ColorJitter.\n\n    Args:\n        brightness (tuple[float, float] | float): How much to jitter brightness.\n            If float:\n                The brightness factor is chosen uniformly from [max(0, 1 - brightness), 1 + brightness].\n            If tuple:\n                The brightness factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        contrast (tuple[float, float] | float): How much to jitter contrast.\n            If float:\n                The contrast factor is chosen uniformly from [max(0, 1 - contrast), 1 + contrast].\n            If tuple:\n                The contrast factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        saturation (tuple[float, float] | float): How much to jitter saturation.\n            If float:\n                The saturation factor is chosen uniformly from [max(0, 1 - saturation), 1 + saturation].\n            If tuple:\n                The saturation factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        hue (float or tuple of float (min, max)): How much to jitter hue.\n            If float:\n                The hue factor is chosen uniformly from [-hue, hue]. Should have 0 <= hue <= 0.5.\n            If tuple:\n                The hue factor is sampled from the range specified. Values should be in range [-0.5, 0.5].\n            Default: (-0.5, 0.5)\n\n         p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1, 3\n\n    Note:\n        - The order of application for these color transformations is random for each image.\n        - The ranges for brightness, contrast, and saturation are applied as multiplicative factors.\n        - The range for hue is applied as an additive factor.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> jittered_image = result['image']\n\n    References:\n        - https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.ColorJitter\n        - https://docs.opencv.org/3.4/de/d25/imgproc_color_conversions.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness: ScaleFloatType\n        contrast: ScaleFloatType\n        saturation: ScaleFloatType\n        hue: ScaleFloatType\n\n        @field_validator(\"brightness\", \"contrast\", \"saturation\", \"hue\")\n        @classmethod\n        def check_ranges(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            if info.field_name == \"hue\":\n                bounds = -0.5, 0.5\n                bias = 0\n                clip = False\n            elif info.field_name in [\"brightness\", \"contrast\", \"saturation\"]:\n                bounds = 0, float(\"inf\")\n                bias = 1\n                clip = True\n\n            if isinstance(value, numbers.Number):\n                if value < 0:\n                    raise ValueError(\n                        f\"If {info.field_name} is a single number, it must be non negative.\",\n                    )\n                left = bias - value\n                if clip:\n                    left = max(left, 0)\n                value = (left, bias + value)\n            elif isinstance(value, tuple) and len(value) == PAIR:\n                check_range(value, *bounds, info.field_name)\n\n            return cast(tuple[float, float], value)\n\n    def __init__(\n        self,\n        brightness: ScaleFloatType = (0.8, 1.2),\n        contrast: ScaleFloatType = (0.8, 1.2),\n        saturation: ScaleFloatType = (0.8, 1.2),\n        hue: ScaleFloatType = (-0.5, 0.5),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.brightness = cast(tuple[float, float], brightness)\n        self.contrast = cast(tuple[float, float], contrast)\n        self.saturation = cast(tuple[float, float], saturation)\n        self.hue = cast(tuple[float, float], hue)\n\n        self.transforms = [\n            fmain.adjust_brightness_torchvision,\n            fmain.adjust_contrast_torchvision,\n            fmain.adjust_saturation_torchvision,\n            fmain.adjust_hue_torchvision,\n        ]\n\n    def get_params(self) -> dict[str, Any]:\n        brightness = self.py_random.uniform(*self.brightness)\n        contrast = self.py_random.uniform(*self.contrast)\n        saturation = self.py_random.uniform(*self.saturation)\n        hue = self.py_random.uniform(*self.hue)\n\n        order = [0, 1, 2, 3]\n        self.random_generator.shuffle(order)\n\n        return {\n            \"brightness\": brightness,\n            \"contrast\": contrast,\n            \"saturation\": saturation,\n            \"hue\": hue,\n            \"order\": order,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        brightness: float,\n        contrast: float,\n        saturation: float,\n        hue: float,\n        order: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"ColorJitter transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n        color_transforms = [brightness, contrast, saturation, hue]\n        for i in order:\n            img = self.transforms[i](img, color_transforms[i])\n        return img\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"brightness\", \"contrast\", \"saturation\", \"hue\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Downscale","title":"class Downscale (scale_min=None, scale_max=None, interpolation=None, scale_range=(0.25, 0.25), interpolation_pair={'upscale': 0, 'downscale': 0}, always_apply=None, p=0.5) [view source on GitHub]","text":"

Decrease image quality by downscaling and upscaling back.

This transform simulates the effect of a low-resolution image by first downscaling the image to a lower resolution and then upscaling it back to its original size. This process introduces loss of detail and can be used to simulate low-quality images or to test the robustness of models to different image resolutions.

Parameters:

Name Type Description scale_range tuple[float, float]

Range for the downscaling factor. Should be two float values between 0 and 1, where the first value is less than or equal to the second. The actual downscaling factor will be randomly chosen from this range for each image. Lower values result in more aggressive downscaling. Default: (0.25, 0.25)

interpolation_pair InterpolationDict

A dictionary specifying the interpolation methods to use for downscaling and upscaling. Should contain two keys: - 'downscale': Interpolation method for downscaling - 'upscale': Interpolation method for upscaling Values should be OpenCV interpolation flags (e.g., cv2.INTER_NEAREST, cv2.INTER_LINEAR, etc.) Default: {'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_NEAREST}

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Note

  • The actual downscaling factor is randomly chosen for each image from the range specified in scale_range.
  • Using different interpolation methods for downscaling and upscaling can produce various effects. For example, using INTER_NEAREST for both can create a pixelated look, while using INTER_LINEAR or INTER_CUBIC can produce smoother results.
  • This transform can be useful for data augmentation, especially when training models that need to be robust to variations in image quality or resolution.

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> transform = A.Downscale(\n...     scale_range=(0.5, 0.75),\n...     interpolation_pair={'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_LINEAR},\n...     p=0.5\n... )\n>>> transformed = transform(image=image)\n>>> downscaled_image = transformed['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Downscale(ImageOnlyTransform):\n    \"\"\"Decrease image quality by downscaling and upscaling back.\n\n    This transform simulates the effect of a low-resolution image by first downscaling\n    the image to a lower resolution and then upscaling it back to its original size.\n    This process introduces loss of detail and can be used to simulate low-quality\n    images or to test the robustness of models to different image resolutions.\n\n    Args:\n        scale_range (tuple[float, float]): Range for the downscaling factor.\n            Should be two float values between 0 and 1, where the first value is less than or equal to the second.\n            The actual downscaling factor will be randomly chosen from this range for each image.\n            Lower values result in more aggressive downscaling.\n            Default: (0.25, 0.25)\n\n        interpolation_pair (InterpolationDict): A dictionary specifying the interpolation methods to use for\n            downscaling and upscaling. Should contain two keys:\n            - 'downscale': Interpolation method for downscaling\n            - 'upscale': Interpolation method for upscaling\n            Values should be OpenCV interpolation flags (e.g., cv2.INTER_NEAREST, cv2.INTER_LINEAR, etc.)\n            Default: {'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_NEAREST}\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual downscaling factor is randomly chosen for each image from the range\n          specified in scale_range.\n        - Using different interpolation methods for downscaling and upscaling can produce\n          various effects. For example, using INTER_NEAREST for both can create a pixelated look,\n          while using INTER_LINEAR or INTER_CUBIC can produce smoother results.\n        - This transform can be useful for data augmentation, especially when training models\n          that need to be robust to variations in image quality or resolution.\n\n    Example:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> transform = A.Downscale(\n        ...     scale_range=(0.5, 0.75),\n        ...     interpolation_pair={'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_LINEAR},\n        ...     p=0.5\n        ... )\n        >>> transformed = transform(image=image)\n        >>> downscaled_image = transformed['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_min: float | None\n        scale_max: float | None\n\n        interpolation: int | Interpolation | InterpolationDict | None = Field(\n            default_factory=lambda: Interpolation(\n                downscale=cv2.INTER_NEAREST,\n                upscale=cv2.INTER_NEAREST,\n            ),\n        )\n        interpolation_pair: InterpolationPydantic\n\n        scale_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_params(self) -> Self:\n            if self.scale_min is not None and self.scale_max is not None:\n                warn(\n                    \"scale_min and scale_max are deprecated. Use scale_range instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n                self.scale_range = (self.scale_min, self.scale_max)\n                self.scale_min = None\n                self.scale_max = None\n\n            if self.interpolation is not None:\n                warn(\n                    \"Downscale.interpolation is deprecated. Use Downscale.interpolation_pair instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n                if isinstance(self.interpolation, dict):\n                    self.interpolation_pair = InterpolationPydantic(\n                        **self.interpolation,\n                    )\n                elif isinstance(self.interpolation, int):\n                    self.interpolation_pair = InterpolationPydantic(\n                        upscale=self.interpolation,\n                        downscale=self.interpolation,\n                    )\n                elif isinstance(self.interpolation, Interpolation):\n                    self.interpolation_pair = InterpolationPydantic(\n                        upscale=self.interpolation.upscale,\n                        downscale=self.interpolation.downscale,\n                    )\n                self.interpolation = None\n\n            return self\n\n    def __init__(\n        self,\n        scale_min: float | None = None,\n        scale_max: float | None = None,\n        interpolation: int | Interpolation | InterpolationDict | None = None,\n        scale_range: tuple[float, float] = (0.25, 0.25),\n        interpolation_pair: InterpolationDict = InterpolationDict(\n            {\"upscale\": cv2.INTER_NEAREST, \"downscale\": cv2.INTER_NEAREST},\n        ),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_range = scale_range\n        self.interpolation_pair = interpolation_pair\n\n    def apply(self, img: np.ndarray, scale: float, **params: Any) -> np.ndarray:\n        return fmain.downscale(\n            img,\n            scale=scale,\n            down_interpolation=self.interpolation_pair[\"downscale\"],\n            up_interpolation=self.interpolation_pair[\"upscale\"],\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\"scale\": self.py_random.uniform(*self.scale_range)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"scale_range\", \"interpolation_pair\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Emboss","title":"class Emboss (alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply embossing effect to the input image.

This transform creates an emboss effect by highlighting edges and creating a 3D-like texture in the image. It works by applying a specific convolution kernel to the image that emphasizes differences in adjacent pixel values.

Parameters:

Name Type Description alpha tuple[float, float]

Range to choose the visibility of the embossed image. At 0, only the original image is visible, at 1.0 only its embossed version is visible. Values should be in the range [0, 1]. Alpha will be randomly selected from this range for each image. Default: (0.2, 0.5)

strength tuple[float, float]

Range to choose the strength of the embossing effect. Higher values create a more pronounced 3D effect. Values should be non-negative. Strength will be randomly selected from this range for each image. Default: (0.2, 0.7)

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Note

  • The emboss effect is created using a 3x3 convolution kernel.
  • The 'alpha' parameter controls the blend between the original image and the embossed version. A higher alpha value will result in a more pronounced emboss effect.
  • The 'strength' parameter affects the intensity of the embossing. Higher strength values will create more contrast in the embossed areas, resulting in a stronger 3D-like effect.
  • This transform can be useful for creating artistic effects or for data augmentation in tasks where edge information is important.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Emboss(alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5)\n>>> result = transform(image=image)\n>>> embossed_image = result['image']\n

References

  • https://en.wikipedia.org/wiki/Image_embossing
  • https://www.researchgate.net/publication/303412455_Application_of_Emboss_Filtering_in_Image_Processing

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Emboss(ImageOnlyTransform):\n    \"\"\"Apply embossing effect to the input image.\n\n    This transform creates an emboss effect by highlighting edges and creating a 3D-like texture\n    in the image. It works by applying a specific convolution kernel to the image that emphasizes\n    differences in adjacent pixel values.\n\n    Args:\n        alpha (tuple[float, float]): Range to choose the visibility of the embossed image.\n            At 0, only the original image is visible, at 1.0 only its embossed version is visible.\n            Values should be in the range [0, 1].\n            Alpha will be randomly selected from this range for each image.\n            Default: (0.2, 0.5)\n\n        strength (tuple[float, float]): Range to choose the strength of the embossing effect.\n            Higher values create a more pronounced 3D effect.\n            Values should be non-negative.\n            Strength will be randomly selected from this range for each image.\n            Default: (0.2, 0.7)\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The emboss effect is created using a 3x3 convolution kernel.\n        - The 'alpha' parameter controls the blend between the original image and the embossed version.\n          A higher alpha value will result in a more pronounced emboss effect.\n        - The 'strength' parameter affects the intensity of the embossing. Higher strength values\n          will create more contrast in the embossed areas, resulting in a stronger 3D-like effect.\n        - This transform can be useful for creating artistic effects or for data augmentation\n          in tasks where edge information is important.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Emboss(alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5)\n        >>> result = transform(image=image)\n        >>> embossed_image = result['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Image_embossing\n        - https://www.researchgate.net/publication/303412455_Application_of_Emboss_Filtering_in_Image_Processing\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        strength: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, None))]\n\n    def __init__(\n        self,\n        alpha: tuple[float, float] = (0.2, 0.5),\n        strength: tuple[float, float] = (0.2, 0.7),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n        self.strength = strength\n\n    @staticmethod\n    def __generate_emboss_matrix(\n        alpha_sample: np.ndarray,\n        strength_sample: np.ndarray,\n    ) -> np.ndarray:\n        matrix_nochange = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=np.float32)\n        matrix_effect = np.array(\n            [\n                [-1 - strength_sample, 0 - strength_sample, 0],\n                [0 - strength_sample, 1, 0 + strength_sample],\n                [0, 0 + strength_sample, 1 + strength_sample],\n            ],\n            dtype=np.float32,\n        )\n        return (1 - alpha_sample) * matrix_nochange + alpha_sample * matrix_effect\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        alpha = self.py_random.uniform(*self.alpha)\n        strength = self.py_random.uniform(*self.strength)\n        emboss_matrix = self.__generate_emboss_matrix(\n            alpha_sample=alpha,\n            strength_sample=strength,\n        )\n        return {\"emboss_matrix\": emboss_matrix}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        emboss_matrix: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.convolve(img, emboss_matrix)\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"alpha\", \"strength\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Equalize","title":"class Equalize (mode='cv', by_channels=True, mask=None, mask_params=(), always_apply=None, p=0.5) [view source on GitHub]","text":"

Equalize the image histogram.

This transform applies histogram equalization to the input image. Histogram equalization is a method in image processing of contrast adjustment using the image's histogram.

Parameters:

Name Type Description mode Literal['cv', 'pil']

Use OpenCV or Pillow equalization method. Default: 'cv'

by_channels bool

If True, use equalization by channels separately, else convert image to YCbCr representation and use equalization by Y channel. Default: True

mask np.ndarray, callable

If given, only the pixels selected by the mask are included in the analysis. Can be: - A 1-channel or 3-channel numpy array of the same size as the input image. - A callable (function) that generates a mask. The function should accept 'image' as its first argument, and can accept additional arguments specified in mask_params. Default: None

mask_params list[str]

Additional parameters to pass to the mask function. These parameters will be taken from the data dict passed to call. Default: ()

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Note

  • When mode='cv', OpenCV's equalizeHist() function is used.
  • When mode='pil', Pillow's equalize() function is used.
  • The 'by_channels' parameter determines whether equalization is applied to each color channel independently (True) or to the luminance channel only (False).
  • If a mask is provided as a numpy array, it should have the same height and width as the input image.
  • If a mask is provided as a function, it allows for dynamic mask generation based on the input image and additional parameters. This is useful for scenarios where the mask depends on the image content or external data (e.g., bounding boxes, segmentation masks).

Mask Function: When mask is a callable, it should have the following signature: mask_func(image, *args) -> np.ndarray

- image: The input image (numpy array)\n- *args: Additional arguments as specified in mask_params\n\nThe function should return a numpy array of the same height and width as the input image,\nwhere non-zero pixels indicate areas to be equalized.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>>\n>>> # Using a static mask\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> transform = A.Equalize(mask=mask, p=1.0)\n>>> result = transform(image=image)\n>>>\n>>> # Using a dynamic mask function\n>>> def mask_func(image, bboxes):\n...     mask = np.ones_like(image[:, :, 0], dtype=np.uint8)\n...     for bbox in bboxes:\n...         x1, y1, x2, y2 = map(int, bbox)\n...         mask[y1:y2, x1:x2] = 0  # Exclude areas inside bounding boxes\n...     return mask\n>>>\n>>> transform = A.Equalize(mask=mask_func, mask_params=['bboxes'], p=1.0)\n>>> bboxes = [(10, 10, 50, 50), (60, 60, 90, 90)]  # Example bounding boxes\n>>> result = transform(image=image, bboxes=bboxes)\n

References

  • OpenCV equalizeHist: https://docs.opencv.org/3.4/d6/dc7/group__imgproc__hist.html#ga7e54091f0c937d49bf84152a16f76d6e
  • Pillow ImageOps.equalize: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.equalize
  • Histogram Equalization: https://en.wikipedia.org/wiki/Histogram_equalization

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Equalize(ImageOnlyTransform):\n    \"\"\"Equalize the image histogram.\n\n    This transform applies histogram equalization to the input image. Histogram equalization\n    is a method in image processing of contrast adjustment using the image's histogram.\n\n    Args:\n        mode (Literal['cv', 'pil']): Use OpenCV or Pillow equalization method.\n            Default: 'cv'\n        by_channels (bool): If True, use equalization by channels separately,\n            else convert image to YCbCr representation and use equalization by `Y` channel.\n            Default: True\n        mask (np.ndarray, callable): If given, only the pixels selected by\n            the mask are included in the analysis. Can be:\n            - A 1-channel or 3-channel numpy array of the same size as the input image.\n            - A callable (function) that generates a mask. The function should accept 'image'\n              as its first argument, and can accept additional arguments specified in mask_params.\n            Default: None\n        mask_params (list[str]): Additional parameters to pass to the mask function.\n            These parameters will be taken from the data dict passed to __call__.\n            Default: ()\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - When mode='cv', OpenCV's equalizeHist() function is used.\n        - When mode='pil', Pillow's equalize() function is used.\n        - The 'by_channels' parameter determines whether equalization is applied to each color channel\n          independently (True) or to the luminance channel only (False).\n        - If a mask is provided as a numpy array, it should have the same height and width as the input image.\n        - If a mask is provided as a function, it allows for dynamic mask generation based on the input image\n          and additional parameters. This is useful for scenarios where the mask depends on the image content\n          or external data (e.g., bounding boxes, segmentation masks).\n\n    Mask Function:\n        When mask is a callable, it should have the following signature:\n        mask_func(image, *args) -> np.ndarray\n\n        - image: The input image (numpy array)\n        - *args: Additional arguments as specified in mask_params\n\n        The function should return a numpy array of the same height and width as the input image,\n        where non-zero pixels indicate areas to be equalized.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>>\n        >>> # Using a static mask\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> transform = A.Equalize(mask=mask, p=1.0)\n        >>> result = transform(image=image)\n        >>>\n        >>> # Using a dynamic mask function\n        >>> def mask_func(image, bboxes):\n        ...     mask = np.ones_like(image[:, :, 0], dtype=np.uint8)\n        ...     for bbox in bboxes:\n        ...         x1, y1, x2, y2 = map(int, bbox)\n        ...         mask[y1:y2, x1:x2] = 0  # Exclude areas inside bounding boxes\n        ...     return mask\n        >>>\n        >>> transform = A.Equalize(mask=mask_func, mask_params=['bboxes'], p=1.0)\n        >>> bboxes = [(10, 10, 50, 50), (60, 60, 90, 90)]  # Example bounding boxes\n        >>> result = transform(image=image, bboxes=bboxes)\n\n    References:\n        - OpenCV equalizeHist: https://docs.opencv.org/3.4/d6/dc7/group__imgproc__hist.html#ga7e54091f0c937d49bf84152a16f76d6e\n        - Pillow ImageOps.equalize: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.equalize\n        - Histogram Equalization: https://en.wikipedia.org/wiki/Histogram_equalization\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: ImageMode\n        by_channels: bool\n        mask: np.ndarray | Callable[..., Any] | None\n        mask_params: Sequence[str]\n\n    def __init__(\n        self,\n        mode: ImageMode = \"cv\",\n        by_channels: bool = True,\n        mask: np.ndarray | Callable[..., Any] | None = None,\n        mask_params: Sequence[str] = (),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.mode = mode\n        self.by_channels = by_channels\n        self.mask = mask\n        self.mask_params = mask_params\n\n    def apply(self, img: np.ndarray, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.equalize(\n            img,\n            mode=self.mode,\n            by_channels=self.by_channels,\n            mask=mask,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if not callable(self.mask):\n            return {\"mask\": self.mask}\n\n        mask_params = {\"image\": data[\"image\"]}\n        for key in self.mask_params:\n            if key not in data:\n                raise KeyError(\n                    f\"Required parameter '{key}' for mask function is missing in data.\",\n                )\n            mask_params[key] = data[key]\n\n        return {\"mask\": self.mask(**mask_params)}\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [*list(self.mask_params)]\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mode\", \"by_channels\", \"mask\", \"mask_params\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.FancyPCA","title":"class FancyPCA (alpha=0.1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Fancy PCA augmentation to the input image.

This augmentation technique applies PCA (Principal Component Analysis) to the image's color channels, then adds multiples of the principal components to the image, with magnitudes proportional to the corresponding eigenvalues times a random variable drawn from a Gaussian with mean 0 and standard deviation 'alpha'.

Parameters:

Name Type Description alpha tuple[float, float] | float

Standard deviation of the Gaussian distribution used to generate random noise for each principal component. If a single float is provided, it will be used for all channels. If a tuple of two floats (min, max) is provided, the standard deviation will be uniformly sampled from this range for each run. Default: 0.1.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: any

Note

  • This augmentation is particularly effective for RGB images but can work with any number of channels.
  • For grayscale images, it applies a simplified version of the augmentation.
  • The transform preserves the mean of the image while adjusting the color/intensity variation.
  • This implementation is based on the paper by Krizhevsky et al. and is similar to the one used in the original AlexNet paper.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.FancyPCA(alpha=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> augmented_image = result[\"image\"]\n

References

  • Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).
  • https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class FancyPCA(ImageOnlyTransform):\n    \"\"\"Apply Fancy PCA augmentation to the input image.\n\n    This augmentation technique applies PCA (Principal Component Analysis) to the image's color channels,\n    then adds multiples of the principal components to the image, with magnitudes proportional to the\n    corresponding eigenvalues times a random variable drawn from a Gaussian with mean 0 and standard\n    deviation 'alpha'.\n\n    Args:\n        alpha (tuple[float, float] | float): Standard deviation of the Gaussian distribution used to generate\n            random noise for each principal component. If a single float is provided, it will be used for\n            all channels. If a tuple of two floats (min, max) is provided, the standard deviation will be\n            uniformly sampled from this range for each run. Default: 0.1.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n\n    Note:\n        - This augmentation is particularly effective for RGB images but can work with any number of channels.\n        - For grayscale images, it applies a simplified version of the augmentation.\n        - The transform preserves the mean of the image while adjusting the color/intensity variation.\n        - This implementation is based on the paper by Krizhevsky et al. and is similar to the one used\n          in the original AlexNet paper.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.FancyPCA(alpha=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> augmented_image = result[\"image\"]\n\n    References:\n        - Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep\n          convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).\n        - https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: float = Field(ge=0)\n\n    def __init__(\n        self,\n        alpha: float = 0.1,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha_vector: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.fancy_pca(img, alpha_vector)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        shape = params[\"shape\"]\n        num_channels = shape[-1] if len(shape) == NUM_MULTI_CHANNEL_DIMENSIONS else 1\n        alpha_vector = self.random_generator.normal(0, self.alpha, num_channels).astype(\n            np.float32,\n        )\n        return {\"alpha_vector\": alpha_vector}\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"alpha\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.FromFloat","title":"class FromFloat (dtype='uint8', max_value=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

Convert an image from floating point representation to the specified data type.

This transform is designed to convert images from a normalized floating-point representation (typically with values in the range [0, 1]) to other data types, scaling the values appropriately.

Parameters:

Name Type Description dtype str

The desired output data type. Supported types include 'uint8', 'uint16', 'uint32'. Default: 'uint8'.

max_value float | None

The maximum value for the output dtype. If None, the transform will attempt to infer the maximum value based on the dtype. Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: float32, float64

Note

  • This is the inverse transform for ToFloat.
  • Input images are expected to be in floating point format with values in the range [0, 1].
  • For integer output types (uint8, uint16, uint32), the function will scale the values to the appropriate range (e.g., 0-255 for uint8).
  • For float output types (float32, float64), the values will remain in the [0, 1] range.
  • The transform uses the from_float function internally, which ensures output values are within the valid range for the specified dtype.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> transform = A.FromFloat(dtype='uint8', max_value=None, p=1.0)\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)  # Float image in [0, 1] range\n>>> result = transform(image=image)\n>>> uint8_image = result['image']\n>>> assert uint8_image.dtype == np.uint8\n>>> assert uint8_image.min() >= 0 and uint8_image.max() <= 255\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class FromFloat(ImageOnlyTransform):\n    \"\"\"Convert an image from floating point representation to the specified data type.\n\n    This transform is designed to convert images from a normalized floating-point representation\n    (typically with values in the range [0, 1]) to other data types, scaling the values appropriately.\n\n    Args:\n        dtype (str): The desired output data type. Supported types include 'uint8', 'uint16',\n                     'uint32'. Default: 'uint8'.\n        max_value (float | None): The maximum value for the output dtype. If None, the transform\n                                  will attempt to infer the maximum value based on the dtype.\n                                  Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        float32, float64\n\n    Note:\n        - This is the inverse transform for ToFloat.\n        - Input images are expected to be in floating point format with values in the range [0, 1].\n        - For integer output types (uint8, uint16, uint32), the function will scale the values\n          to the appropriate range (e.g., 0-255 for uint8).\n        - For float output types (float32, float64), the values will remain in the [0, 1] range.\n        - The transform uses the `from_float` function internally, which ensures output values\n          are within the valid range for the specified dtype.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> transform = A.FromFloat(dtype='uint8', max_value=None, p=1.0)\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)  # Float image in [0, 1] range\n        >>> result = transform(image=image)\n        >>> uint8_image = result['image']\n        >>> assert uint8_image.dtype == np.uint8\n        >>> assert uint8_image.min() >= 0 and uint8_image.max() <= 255\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        dtype: Literal[\"uint8\", \"uint16\", \"float32\", \"float64\"]\n        max_value: float | None\n\n    def __init__(\n        self,\n        dtype: Literal[\"uint8\", \"uint16\", \"float32\", \"float64\"] = \"uint8\",\n        max_value: float | None = None,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.dtype = np.dtype(dtype)\n        self.max_value = max_value\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return from_float(img, self.dtype, self.max_value)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\"dtype\": self.dtype.name, \"max_value\": self.max_value}\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GaussNoise","title":"class GaussNoise (var_limit=None, mean=None, std_range=(0.2, 0.44), mean_range=(0.0, 0.0), per_channel=True, noise_scale_factor=1, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Gaussian noise to the input image.

Parameters:

Name Type Description std_range tuple[float, float]

Range for noise standard deviation as a fraction of the maximum value (255 for uint8 images or 1.0 for float images). Values should be in range [0, 1]. Default: (0.2, 0.44).

mean_range tuple[float, float]

Range for noise mean as a fraction of the maximum value (255 for uint8 images or 1.0 for float images). Values should be in range [-1, 1]. Default: (0.0, 0.0).

var_limit tuple[float, float] | float

[Deprecated] Variance range for noise. If var_limit is a single float value, the range will be (0, var_limit). Default: (10.0, 50.0).

mean float

[Deprecated] Mean of the noise. Default: 0.

per_channel bool

If True, noise will be sampled for each channel independently. Otherwise, the noise will be sampled once for all channels. Default: True.

noise_scale_factor float

Scaling factor for noise generation. Value should be in the range (0, 1]. When set to 1, noise is sampled for each pixel independently. If less, noise is sampled for a smaller size and resized to fit the shape of the image. Smaller values make the transform faster. Default: 1.0.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The noise parameters (std_range and mean_range) are normalized to [0, 1] range:
  • For uint8 images, they are multiplied by 255
  • For float32 images, they are used directly
  • The behavior differs between old and new parameters:
  • When using var_limit (deprecated): samples variance uniformly and takes sqrt to get std dev
  • When using std_range: samples standard deviation directly (aligned with torchvision/kornia)
  • Setting per_channel=False is faster but applies the same noise to all channels
  • The noise_scale_factor parameter allows for a trade-off between transform speed and noise granularity

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)\n>>>\n>>> # Apply Gaussian noise with normalized std_range\n>>> transform = A.GaussNoise(std_range=(0.1, 0.2), p=1.0)  # 10-20% of max value\n>>> noisy_image = transform(image=image)['image']\n>>>\n>>> # Using deprecated var_limit (will be converted to std_range)\n>>> transform = A.GaussNoise(var_limit=(50.0, 100.0), mean=10, p=1.0)\n>>> noisy_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class GaussNoise(ImageOnlyTransform):\n    \"\"\"Apply Gaussian noise to the input image.\n\n    Args:\n        std_range (tuple[float, float]): Range for noise standard deviation as a fraction\n            of the maximum value (255 for uint8 images or 1.0 for float images).\n            Values should be in range [0, 1]. Default: (0.2, 0.44).\n        mean_range (tuple[float, float]): Range for noise mean as a fraction\n            of the maximum value (255 for uint8 images or 1.0 for float images).\n            Values should be in range [-1, 1]. Default: (0.0, 0.0).\n        var_limit (tuple[float, float] | float): [Deprecated] Variance range for noise.\n            If var_limit is a single float value, the range will be (0, var_limit).\n            Default: (10.0, 50.0).\n        mean (float): [Deprecated] Mean of the noise. Default: 0.\n        per_channel (bool): If True, noise will be sampled for each channel independently.\n            Otherwise, the noise will be sampled once for all channels. Default: True.\n        noise_scale_factor (float): Scaling factor for noise generation. Value should be in the range (0, 1].\n            When set to 1, noise is sampled for each pixel independently. If less, noise is sampled for a smaller size\n            and resized to fit the shape of the image. Smaller values make the transform faster. Default: 1.0.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The noise parameters (std_range and mean_range) are normalized to [0, 1] range:\n          * For uint8 images, they are multiplied by 255\n          * For float32 images, they are used directly\n        - The behavior differs between old and new parameters:\n          * When using var_limit (deprecated): samples variance uniformly and takes sqrt to get std dev\n          * When using std_range: samples standard deviation directly (aligned with torchvision/kornia)\n        - Setting per_channel=False is faster but applies the same noise to all channels\n        - The noise_scale_factor parameter allows for a trade-off between transform speed and noise granularity\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)\n        >>>\n        >>> # Apply Gaussian noise with normalized std_range\n        >>> transform = A.GaussNoise(std_range=(0.1, 0.2), p=1.0)  # 10-20% of max value\n        >>> noisy_image = transform(image=image)['image']\n        >>>\n        >>> # Using deprecated var_limit (will be converted to std_range)\n        >>> transform = A.GaussNoise(var_limit=(50.0, 100.0), mean=10, p=1.0)\n        >>> noisy_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        var_limit: ScaleFloatType | None\n        mean: float | None\n        std_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        mean_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        per_channel: bool\n        noise_scale_factor: float = Field(gt=0, le=1)\n\n        @model_validator(mode=\"after\")\n        def check_range(self) -> Self:\n            if self.var_limit is not None:\n                warnings.warn(\"`var_limit` deprecated. Use `std_range` instead.\", DeprecationWarning, stacklevel=2)\n                self.var_limit = to_tuple(self.var_limit, 0)\n                if self.var_limit[1] > 1:\n                    # Convert legacy uint8 variance to normalized std dev\n                    self.std_range = (math.sqrt(10 / 255), math.sqrt(50 / 255))\n                else:\n                    # Already normalized variance, convert to std dev\n                    self.std_range = (\n                        math.sqrt(self.var_limit[0]),\n                        math.sqrt(self.var_limit[1]),\n                    )\n\n            if self.mean is not None:\n                warn(\"`mean` deprecated. Use `mean_range` instead.\", DeprecationWarning, stacklevel=2)\n                if self.mean >= 1:\n                    # Convert legacy uint8 mean to normalized range\n                    self.mean_range = (self.mean / 255, self.mean / 255)\n                else:\n                    # Already normalized mean\n                    self.mean_range = (self.mean, self.mean)\n\n            return self\n\n    def __init__(\n        self,\n        var_limit: ScaleFloatType | None = None,\n        mean: float | None = None,\n        std_range: tuple[float, float] = (0.2, 0.44),  # sqrt(10 / 255), sqrt(50 / 255)\n        mean_range: tuple[float, float] = (0.0, 0.0),\n        per_channel: bool = True,\n        noise_scale_factor: float = 1,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.std_range = std_range\n        self.mean_range = mean_range\n        self.per_channel = per_channel\n        self.noise_scale_factor = noise_scale_factor\n\n        self.var_limit = var_limit\n\n    def apply(\n        self,\n        img: np.ndarray,\n        noise_map: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_noise(img, noise_map)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, float]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n\n        if self.var_limit is not None:\n            # Legacy behavior: sample variance uniformly then take sqrt\n            var = self.py_random.uniform(self.std_range[0] ** 2, self.std_range[1] ** 2)\n            sigma = math.sqrt(var)\n        else:\n            # New behavior: sample std dev directly (aligned with torchvision/kornia)\n            sigma = self.py_random.uniform(*self.std_range)\n\n        mean = self.py_random.uniform(*self.mean_range)\n\n        noise_map = fmain.generate_noise(\n            noise_type=\"gaussian\",\n            spatial_mode=\"per_pixel\" if self.per_channel else \"shared\",\n            shape=image.shape,\n            params={\"mean_range\": (mean, mean), \"std_range\": (sigma, sigma)},\n            max_value=max_value,\n            approximation=self.noise_scale_factor,\n            random_generator=self.random_generator,\n        )\n\n        return {\"noise_map\": noise_map}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"std_range\", \"mean_range\", \"per_channel\", \"noise_scale_factor\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GaussianParams","title":"class GaussianParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class GaussianParams(NoiseParamsBase):\n    noise_type: Literal[\"gaussian\"] = \"gaussian\"\n    mean_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=-1, max_val=1)),\n    ]\n    std_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.HueSaturationValue","title":"class HueSaturationValue (hue_shift_limit=(-20, 20), sat_shift_limit=(-30, 30), val_shift_limit=(-20, 20), always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly change hue, saturation and value of the input image.

This transform adjusts the HSV (Hue, Saturation, Value) channels of an input RGB image. It allows for independent control over each channel, providing a wide range of color and brightness modifications.

Parameters:

Name Type Description hue_shift_limit float | tuple[float, float]

Range for changing hue. If a single float value is provided, the range will be (-hue_shift_limit, hue_shift_limit). Values should be in the range [-180, 180]. Default: (-20, 20).

sat_shift_limit float | tuple[float, float]

Range for changing saturation. If a single float value is provided, the range will be (-sat_shift_limit, sat_shift_limit). Values should be in the range [-255, 255]. Default: (-30, 30).

val_shift_limit float | tuple[float, float]

Range for changing value (brightness). If a single float value is provided, the range will be (-val_shift_limit, val_shift_limit). Values should be in the range [-255, 255]. Default: (-20, 20).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The transform first converts the input RGB image to the HSV color space.
  • Each channel (Hue, Saturation, Value) is adjusted independently.
  • Hue is circular, so it wraps around at 180 degrees.
  • For float32 images, the shift values are applied as percentages of the full range.
  • This transform is particularly useful for color augmentation and simulating different lighting conditions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.HueSaturationValue(\n...     hue_shift_limit=20,\n...     sat_shift_limit=30,\n...     val_shift_limit=20,\n...     p=0.7\n... )\n>>> result = transform(image=image)\n>>> augmented_image = result[\"image\"]\n

References

  • HSV color space: https://en.wikipedia.org/wiki/HSL_and_HSV

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class HueSaturationValue(ImageOnlyTransform):\n    \"\"\"Randomly change hue, saturation and value of the input image.\n\n    This transform adjusts the HSV (Hue, Saturation, Value) channels of an input RGB image.\n    It allows for independent control over each channel, providing a wide range of color\n    and brightness modifications.\n\n    Args:\n        hue_shift_limit (float | tuple[float, float]): Range for changing hue.\n            If a single float value is provided, the range will be (-hue_shift_limit, hue_shift_limit).\n            Values should be in the range [-180, 180]. Default: (-20, 20).\n\n        sat_shift_limit (float | tuple[float, float]): Range for changing saturation.\n            If a single float value is provided, the range will be (-sat_shift_limit, sat_shift_limit).\n            Values should be in the range [-255, 255]. Default: (-30, 30).\n\n        val_shift_limit (float | tuple[float, float]): Range for changing value (brightness).\n            If a single float value is provided, the range will be (-val_shift_limit, val_shift_limit).\n            Values should be in the range [-255, 255]. Default: (-20, 20).\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The transform first converts the input RGB image to the HSV color space.\n        - Each channel (Hue, Saturation, Value) is adjusted independently.\n        - Hue is circular, so it wraps around at 180 degrees.\n        - For float32 images, the shift values are applied as percentages of the full range.\n        - This transform is particularly useful for color augmentation and simulating\n          different lighting conditions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.HueSaturationValue(\n        ...     hue_shift_limit=20,\n        ...     sat_shift_limit=30,\n        ...     val_shift_limit=20,\n        ...     p=0.7\n        ... )\n        >>> result = transform(image=image)\n        >>> augmented_image = result[\"image\"]\n\n    References:\n        - HSV color space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        hue_shift_limit: SymmetricRangeType\n        sat_shift_limit: SymmetricRangeType\n        val_shift_limit: SymmetricRangeType\n\n    def __init__(\n        self,\n        hue_shift_limit: ScaleFloatType = (-20, 20),\n        sat_shift_limit: ScaleFloatType = (-30, 30),\n        val_shift_limit: ScaleFloatType = (-20, 20),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.hue_shift_limit = cast(tuple[float, float], hue_shift_limit)\n        self.sat_shift_limit = cast(tuple[float, float], sat_shift_limit)\n        self.val_shift_limit = cast(tuple[float, float], val_shift_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        hue_shift: int,\n        sat_shift: int,\n        val_shift: int,\n        **params: Any,\n    ) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"HueSaturationValue transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n        return fmain.shift_hsv(img, hue_shift, sat_shift, val_shift)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"hue_shift\": self.py_random.uniform(*self.hue_shift_limit),\n            \"sat_shift\": self.py_random.uniform(*self.sat_shift_limit),\n            \"val_shift\": self.py_random.uniform(*self.val_shift_limit),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"hue_shift_limit\", \"sat_shift_limit\", \"val_shift_limit\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ISONoise","title":"class ISONoise (color_shift=(0.01, 0.05), intensity=(0.1, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies camera sensor noise to the input image, simulating high ISO settings.

This transform adds random noise to an image, mimicking the effect of using high ISO settings in digital photography. It simulates two main components of ISO noise: 1. Color noise: random shifts in color hue 2. Luminance noise: random variations in pixel intensity

Parameters:

Name Type Description color_shift tuple[float, float]

Range for changing color hue. Values should be in the range [0, 1], where 1 represents a full 360\u00b0 hue rotation. Default: (0.01, 0.05)

intensity tuple[float, float]

Range for the noise intensity. Higher values increase the strength of both color and luminance noise. Default: (0.1, 0.5)

p float

Probability of applying the transform. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: 3

Note

  • This transform only works with RGB images. It will raise a TypeError if applied to non-RGB images.
  • The color shift is applied in the HSV color space, affecting the hue channel.
  • Luminance noise is added to all channels independently.
  • This transform can be useful for data augmentation in low-light scenarios or when training models to be robust against noisy inputs.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=0.5)\n>>> result = transform(image=image)\n>>> noisy_image = result[\"image\"]\n

References

  • ISO noise in digital photography: https://en.wikipedia.org/wiki/Image_noise#In_digital_cameras

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ISONoise(ImageOnlyTransform):\n    \"\"\"Applies camera sensor noise to the input image, simulating high ISO settings.\n\n    This transform adds random noise to an image, mimicking the effect of using high ISO settings\n    in digital photography. It simulates two main components of ISO noise:\n    1. Color noise: random shifts in color hue\n    2. Luminance noise: random variations in pixel intensity\n\n    Args:\n        color_shift (tuple[float, float]): Range for changing color hue.\n            Values should be in the range [0, 1], where 1 represents a full 360\u00b0 hue rotation.\n            Default: (0.01, 0.05)\n\n        intensity (tuple[float, float]): Range for the noise intensity.\n            Higher values increase the strength of both color and luminance noise.\n            Default: (0.1, 0.5)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - This transform only works with RGB images. It will raise a TypeError if applied to\n          non-RGB images.\n        - The color shift is applied in the HSV color space, affecting the hue channel.\n        - Luminance noise is added to all channels independently.\n        - This transform can be useful for data augmentation in low-light scenarios or when\n          training models to be robust against noisy inputs.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=0.5)\n        >>> result = transform(image=image)\n        >>> noisy_image = result[\"image\"]\n\n    References:\n        - ISO noise in digital photography:\n          https://en.wikipedia.org/wiki/Image_noise#In_digital_cameras\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        color_shift: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        intensity: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n\n    def __init__(\n        self,\n        color_shift: tuple[float, float] = (0.01, 0.05),\n        intensity: tuple[float, float] = (0.1, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.intensity = intensity\n        self.color_shift = color_shift\n\n    def apply(\n        self,\n        img: np.ndarray,\n        color_shift: float,\n        intensity: float,\n        random_seed: int,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.iso_noise(\n            img,\n            color_shift,\n            intensity,\n            np.random.default_rng(random_seed),\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        random_seed = self.random_generator.integers(0, 2**32 - 1)\n        return {\n            \"color_shift\": self.py_random.uniform(*self.color_shift),\n            \"intensity\": self.py_random.uniform(*self.intensity),\n            \"random_seed\": random_seed,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"intensity\", \"color_shift\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Illumination","title":"class Illumination (mode='linear', intensity_range=(0.01, 0.2), effect_type='both', angle_range=(0, 360), center_range=(0.1, 0.9), sigma_range=(0.2, 1.0), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply various illumination effects to the image.

This transform simulates different lighting conditions by applying controlled illumination patterns. It can create effects like: - Directional lighting (linear mode) - Corner shadows/highlights (corner mode) - Spotlights or local lighting (gaussian mode)

These effects can be used to: - Simulate natural lighting variations - Add dramatic lighting effects - Create synthetic shadows or highlights - Augment training data with different lighting conditions

Parameters:

Name Type Description mode Literal[\"linear\", \"corner\", \"gaussian\"]

Type of illumination pattern: - 'linear': Creates a smooth gradient across the image, simulating directional lighting like sunlight through a window - 'corner': Applies gradient from any corner, simulating light source from a corner - 'gaussian': Creates a circular spotlight effect, simulating local light sources Default: 'linear'

intensity_range tuple[float, float]

Range for effect strength. Values between 0.01 and 0.2: - 0.01-0.05: Subtle lighting changes - 0.05-0.1: Moderate lighting effects - 0.1-0.2: Strong lighting effects Default: (0.01, 0.2)

effect_type str

Type of lighting change: - 'brighten': Only adds light (like a spotlight) - 'darken': Only removes light (like a shadow) - 'both': Randomly chooses between brightening and darkening Default: 'both'

angle_range tuple[float, float]

Range for gradient angle in degrees. Controls direction of linear gradient: - 0\u00b0: Left to right - 90\u00b0: Top to bottom - 180\u00b0: Right to left - 270\u00b0: Bottom to top Only used for 'linear' mode. Default: (0, 360)

center_range tuple[float, float]

Range for spotlight position. Values between 0 and 1 representing relative position: - (0, 0): Top-left corner - (1, 1): Bottom-right corner - (0.5, 0.5): Center of image Only used for 'gaussian' mode. Default: (0.1, 0.9)

sigma_range tuple[float, float]

Range for spotlight size. Values between 0.2 and 1.0: - 0.2: Small, focused spotlight - 0.5: Medium-sized light area - 1.0: Broad, soft lighting Only used for 'gaussian' mode. Default: (0.2, 1.0)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import albumentations as A\n>>> # Simulate sunlight through window\n>>> transform = A.Illumination(\n...     mode='linear',\n...     intensity_range=(0.05, 0.1),\n...     effect_type='brighten',\n...     angle_range=(30, 60)\n... )\n>>>\n>>> # Create dramatic corner shadow\n>>> transform = A.Illumination(\n...     mode='corner',\n...     intensity_range=(0.1, 0.2),\n...     effect_type='darken'\n... )\n>>>\n>>> # Add multiple spotlights\n>>> transform1 = A.Illumination(\n...     mode='gaussian',\n...     intensity_range=(0.05, 0.15),\n...     effect_type='brighten',\n...     center_range=(0.2, 0.4),\n...     sigma_range=(0.2, 0.3)\n... )\n>>> transform2 = A.Illumination(\n...     mode='gaussian',\n...     intensity_range=(0.05, 0.15),\n...     effect_type='darken',\n...     center_range=(0.6, 0.8),\n...     sigma_range=(0.3, 0.5)\n... )\n>>> transforms = A.Compose([transform1, transform2])\n

References

  • Lighting in Computer Vision: https://en.wikipedia.org/wiki/Lighting_in_computer_vision

  • Image-based lighting: https://en.wikipedia.org/wiki/Image-based_lighting

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#randomlinearillumination

  • Research on lighting augmentation: \"Learning Deep Representations of Fine-grained Visual Descriptions\" https://arxiv.org/abs/1605.05395

  • Photography lighting patterns: https://en.wikipedia.org/wiki/Lighting_pattern

Note

  • The transform preserves image range and dtype
  • Effects are applied multiplicatively to preserve texture
  • Can be combined with other transforms for complex lighting scenarios
  • Useful for training models to be robust to lighting variations

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Illumination(ImageOnlyTransform):\n    \"\"\"Apply various illumination effects to the image.\n\n    This transform simulates different lighting conditions by applying controlled\n    illumination patterns. It can create effects like:\n    - Directional lighting (linear mode)\n    - Corner shadows/highlights (corner mode)\n    - Spotlights or local lighting (gaussian mode)\n\n    These effects can be used to:\n    - Simulate natural lighting variations\n    - Add dramatic lighting effects\n    - Create synthetic shadows or highlights\n    - Augment training data with different lighting conditions\n\n    Args:\n        mode (Literal[\"linear\", \"corner\", \"gaussian\"]): Type of illumination pattern:\n            - 'linear': Creates a smooth gradient across the image,\n                       simulating directional lighting like sunlight\n                       through a window\n            - 'corner': Applies gradient from any corner,\n                       simulating light source from a corner\n            - 'gaussian': Creates a circular spotlight effect,\n                         simulating local light sources\n            Default: 'linear'\n\n        intensity_range (tuple[float, float]): Range for effect strength.\n            Values between 0.01 and 0.2:\n            - 0.01-0.05: Subtle lighting changes\n            - 0.05-0.1: Moderate lighting effects\n            - 0.1-0.2: Strong lighting effects\n            Default: (0.01, 0.2)\n\n        effect_type (str): Type of lighting change:\n            - 'brighten': Only adds light (like a spotlight)\n            - 'darken': Only removes light (like a shadow)\n            - 'both': Randomly chooses between brightening and darkening\n            Default: 'both'\n\n        angle_range (tuple[float, float]): Range for gradient angle in degrees.\n            Controls direction of linear gradient:\n            - 0\u00b0: Left to right\n            - 90\u00b0: Top to bottom\n            - 180\u00b0: Right to left\n            - 270\u00b0: Bottom to top\n            Only used for 'linear' mode.\n            Default: (0, 360)\n\n        center_range (tuple[float, float]): Range for spotlight position.\n            Values between 0 and 1 representing relative position:\n            - (0, 0): Top-left corner\n            - (1, 1): Bottom-right corner\n            - (0.5, 0.5): Center of image\n            Only used for 'gaussian' mode.\n            Default: (0.1, 0.9)\n\n        sigma_range (tuple[float, float]): Range for spotlight size.\n            Values between 0.2 and 1.0:\n            - 0.2: Small, focused spotlight\n            - 0.5: Medium-sized light area\n            - 1.0: Broad, soft lighting\n            Only used for 'gaussian' mode.\n            Default: (0.2, 1.0)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Examples:\n        >>> import albumentations as A\n        >>> # Simulate sunlight through window\n        >>> transform = A.Illumination(\n        ...     mode='linear',\n        ...     intensity_range=(0.05, 0.1),\n        ...     effect_type='brighten',\n        ...     angle_range=(30, 60)\n        ... )\n        >>>\n        >>> # Create dramatic corner shadow\n        >>> transform = A.Illumination(\n        ...     mode='corner',\n        ...     intensity_range=(0.1, 0.2),\n        ...     effect_type='darken'\n        ... )\n        >>>\n        >>> # Add multiple spotlights\n        >>> transform1 = A.Illumination(\n        ...     mode='gaussian',\n        ...     intensity_range=(0.05, 0.15),\n        ...     effect_type='brighten',\n        ...     center_range=(0.2, 0.4),\n        ...     sigma_range=(0.2, 0.3)\n        ... )\n        >>> transform2 = A.Illumination(\n        ...     mode='gaussian',\n        ...     intensity_range=(0.05, 0.15),\n        ...     effect_type='darken',\n        ...     center_range=(0.6, 0.8),\n        ...     sigma_range=(0.3, 0.5)\n        ... )\n        >>> transforms = A.Compose([transform1, transform2])\n\n    References:\n        - Lighting in Computer Vision:\n          https://en.wikipedia.org/wiki/Lighting_in_computer_vision\n\n        - Image-based lighting:\n          https://en.wikipedia.org/wiki/Image-based_lighting\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#randomlinearillumination\n\n        - Research on lighting augmentation:\n          \"Learning Deep Representations of Fine-grained Visual Descriptions\"\n          https://arxiv.org/abs/1605.05395\n\n        - Photography lighting patterns:\n          https://en.wikipedia.org/wiki/Lighting_pattern\n\n    Note:\n        - The transform preserves image range and dtype\n        - Effects are applied multiplicatively to preserve texture\n        - Can be combined with other transforms for complex lighting scenarios\n        - Useful for training models to be robust to lighting variations\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: Literal[\"linear\", \"corner\", \"gaussian\"]\n        intensity_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0.01, 0.2)),\n        ]\n        effect_type: Literal[\"brighten\", \"darken\", \"both\"]\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 360)),\n        ]\n        center_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        sigma_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0.2, 1.0)),\n        ]\n\n    def __init__(\n        self,\n        mode: Literal[\"linear\", \"corner\", \"gaussian\"] = \"linear\",\n        intensity_range: tuple[float, float] = (0.01, 0.2),\n        effect_type: Literal[\"brighten\", \"darken\", \"both\"] = \"both\",\n        angle_range: tuple[float, float] = (0, 360),\n        center_range: tuple[float, float] = (0.1, 0.9),\n        sigma_range: tuple[float, float] = (0.2, 1.0),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(always_apply=always_apply, p=p)\n        self.mode = mode\n        self.intensity_range = intensity_range\n        self.effect_type = effect_type\n        self.angle_range = angle_range\n        self.center_range = center_range\n        self.sigma_range = sigma_range\n\n    def get_params(self) -> dict[str, Any]:\n        intensity = self.py_random.uniform(*self.intensity_range)\n\n        # Determine if brightening or darkening\n        sign = 1  # brighten\n        if self.effect_type == \"both\":\n            sign = 1 if self.py_random.random() > 0.5 else -1\n        elif self.effect_type == \"darken\":\n            sign = -1\n\n        intensity *= sign\n\n        if self.mode == \"linear\":\n            angle = self.py_random.uniform(*self.angle_range)\n            return {\n                \"intensity\": intensity,\n                \"angle\": angle,\n            }\n        if self.mode == \"corner\":\n            corner = self.py_random.randint(0, 3)  # Choose random corner\n            return {\n                \"intensity\": intensity,\n                \"corner\": corner,\n            }\n\n        x = self.py_random.uniform(*self.center_range)\n        y = self.py_random.uniform(*self.center_range)\n        sigma = self.py_random.uniform(*self.sigma_range)\n        return {\n            \"intensity\": intensity,\n            \"center\": (x, y),\n            \"sigma\": sigma,\n        }\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if self.mode == \"linear\":\n            return fmain.apply_linear_illumination(\n                img,\n                intensity=params[\"intensity\"],\n                angle=params[\"angle\"],\n            )\n        if self.mode == \"corner\":\n            return fmain.apply_corner_illumination(\n                img,\n                intensity=params[\"intensity\"],\n                corner=params[\"corner\"],\n            )\n\n        return fmain.apply_gaussian_illumination(\n            img,\n            intensity=params[\"intensity\"],\n            center=params[\"center\"],\n            sigma=params[\"sigma\"],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"mode\",\n            \"intensity_range\",\n            \"effect_type\",\n            \"angle_range\",\n            \"center_range\",\n            \"sigma_range\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ImageCompression","title":"class ImageCompression (quality_lower=None, quality_upper=None, compression_type='jpeg', quality_range=(99, 100), always_apply=None, p=0.5) [view source on GitHub]","text":"

Decrease image quality by applying JPEG or WebP compression.

This transform simulates the effect of saving an image with lower quality settings, which can introduce compression artifacts. It's useful for data augmentation and for testing model robustness against varying image qualities.

Parameters:

Name Type Description quality_range tuple[int, int]

Range for the compression quality. The values should be in [1, 100] range, where: - 1 is the lowest quality (maximum compression) - 100 is the highest quality (minimum compression) Default: (99, 100)

compression_type Literal[\"jpeg\", \"webp\"]

Type of compression to apply. - \"jpeg\": JPEG compression - \"webp\": WebP compression Default: \"jpeg\"

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform expects images with 1, 3, or 4 channels.
  • For JPEG compression, alpha channels (4th channel) will be ignored.
  • WebP compression supports transparency (4 channels).
  • The actual file is not saved to disk; the compression is simulated in memory.
  • Lower quality values result in smaller file sizes but may introduce visible artifacts.
  • This transform can be useful for:
  • Data augmentation to improve model robustness
  • Testing how models perform on images of varying quality
  • Simulating images transmitted over low-bandwidth connections

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ImageCompression(quality_range=(50, 90), compression_type=0, p=1.0)\n>>> result = transform(image=image)\n>>> compressed_image = result[\"image\"]\n

References

  • JPEG compression: https://en.wikipedia.org/wiki/JPEG
  • WebP compression: https://developers.google.com/speed/webp

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ImageCompression(ImageOnlyTransform):\n    \"\"\"Decrease image quality by applying JPEG or WebP compression.\n\n    This transform simulates the effect of saving an image with lower quality settings,\n    which can introduce compression artifacts. It's useful for data augmentation and\n    for testing model robustness against varying image qualities.\n\n    Args:\n        quality_range (tuple[int, int]): Range for the compression quality.\n            The values should be in [1, 100] range, where:\n            - 1 is the lowest quality (maximum compression)\n            - 100 is the highest quality (minimum compression)\n            Default: (99, 100)\n\n        compression_type (Literal[\"jpeg\", \"webp\"]): Type of compression to apply.\n            - \"jpeg\": JPEG compression\n            - \"webp\": WebP compression\n            Default: \"jpeg\"\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform expects images with 1, 3, or 4 channels.\n        - For JPEG compression, alpha channels (4th channel) will be ignored.\n        - WebP compression supports transparency (4 channels).\n        - The actual file is not saved to disk; the compression is simulated in memory.\n        - Lower quality values result in smaller file sizes but may introduce visible artifacts.\n        - This transform can be useful for:\n          * Data augmentation to improve model robustness\n          * Testing how models perform on images of varying quality\n          * Simulating images transmitted over low-bandwidth connections\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ImageCompression(quality_range=(50, 90), compression_type=0, p=1.0)\n        >>> result = transform(image=image)\n        >>> compressed_image = result[\"image\"]\n\n    References:\n        - JPEG compression: https://en.wikipedia.org/wiki/JPEG\n        - WebP compression: https://developers.google.com/speed/webp\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        quality_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, 100)),\n            AfterValidator(nondecreasing),\n        ]\n\n        quality_lower: int | None = Field(\n            ge=1,\n            le=100,\n        )\n        quality_upper: int | None = Field(\n            ge=1,\n            le=100,\n        )\n        compression_type: Literal[\"jpeg\", \"webp\"]\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            # Update the quality_range based on the non-None values of quality_lower and quality_upper\n            if self.quality_lower is not None or self.quality_upper is not None:\n                if self.quality_lower is not None:\n                    warn(\n                        \"`quality_lower` is deprecated. Use `quality_range` as tuple\"\n                        \" (quality_lower, quality_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.quality_upper is not None:\n                    warn(\n                        \"`quality_upper` is deprecated. Use `quality_range` as tuple\"\n                        \" (quality_lower, quality_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.quality_lower if self.quality_lower is not None else self.quality_range[0]\n                upper = self.quality_upper if self.quality_upper is not None else self.quality_range[1]\n                self.quality_range = (lower, upper)\n                # Clear the deprecated individual quality settings\n                self.quality_lower = None\n                self.quality_upper = None\n\n            # Validate the quality_range\n            if not (1 <= self.quality_range[0] <= MAX_JPEG_QUALITY and 1 <= self.quality_range[1] <= MAX_JPEG_QUALITY):\n                raise ValueError(\n                    f\"Quality range values should be within [1, {MAX_JPEG_QUALITY}] range.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        quality_lower: int | None = None,\n        quality_upper: int | None = None,\n        compression_type: Literal[\"jpeg\", \"webp\"] = \"jpeg\",\n        quality_range: tuple[int, int] = (99, 100),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.quality_range = quality_range\n        self.compression_type = compression_type\n\n    def apply(\n        self,\n        img: np.ndarray,\n        quality: int,\n        image_type: Literal[\".jpg\", \".webp\"],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.image_compression(img, quality, image_type)\n\n    def get_params(self) -> dict[str, int | str]:\n        if self.compression_type == \"jpeg\":\n            image_type = \".jpg\"\n        elif self.compression_type == \"webp\":\n            image_type = \".webp\"\n        else:\n            raise ValueError(f\"Unknown image compression type: {self.compression_type}\")\n\n        return {\n            \"quality\": self.py_random.randint(*self.quality_range),\n            \"image_type\": image_type,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"quality_range\", \"compression_type\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.InterpolationPydantic","title":"class InterpolationPydantic ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class InterpolationPydantic(BaseModel):\n    upscale: InterpolationType\n    downscale: InterpolationType\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.InvertImg","title":"class InvertImg [view source on GitHub]","text":"

Invert the input image by subtracting pixel values from max values of the image types, i.e., 255 for uint8 and 1.0 for float32.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class InvertImg(ImageOnlyTransform):\n    \"\"\"Invert the input image by subtracting pixel values from max values of the image types,\n    i.e., 255 for uint8 and 1.0 for float32.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    \"\"\"\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.invert(img)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Lambda","title":"class Lambda (image=None, mask=None, keypoints=None, bboxes=None, name=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

A flexible transformation class for using user-defined transformation functions per targets. Function signature must include **kwargs to accept optional arguments like interpolation method, image size, etc:

Parameters:

Name Type Description image Callable[..., Any] | None

Image transformation function.

mask Callable[..., Any] | None

Mask transformation function.

keypoints Callable[..., Any] | None

Keypoints transformation function.

bboxes Callable[..., Any] | None

BBoxes transformation function.

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Number of channels: Any

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Lambda(NoOp):\n    \"\"\"A flexible transformation class for using user-defined transformation functions per targets.\n    Function signature must include **kwargs to accept optional arguments like interpolation method, image size, etc:\n\n    Args:\n        image: Image transformation function.\n        mask: Mask transformation function.\n        keypoints: Keypoints transformation function.\n        bboxes: BBoxes transformation function.\n        p: probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    \"\"\"\n\n    def __init__(\n        self,\n        image: Callable[..., Any] | None = None,\n        mask: Callable[..., Any] | None = None,\n        keypoints: Callable[..., Any] | None = None,\n        bboxes: Callable[..., Any] | None = None,\n        name: str | None = None,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.name = name\n        self.custom_apply_fns = {\n            target_name: fmain.noop for target_name in (\"image\", \"mask\", \"keypoints\", \"bboxes\", \"global_label\")\n        }\n        for target_name, custom_apply_fn in {\n            \"image\": image,\n            \"mask\": mask,\n            \"keypoints\": keypoints,\n            \"bboxes\": bboxes,\n        }.items():\n            if custom_apply_fn is not None:\n                if isinstance(custom_apply_fn, LambdaType) and custom_apply_fn.__name__ == \"<lambda>\":\n                    warnings.warn(\n                        \"Using lambda is incompatible with multiprocessing. \"\n                        \"Consider using regular functions or partial().\",\n                        stacklevel=2,\n                    )\n\n                self.custom_apply_fns[target_name] = custom_apply_fn\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        fn = self.custom_apply_fns[\"image\"]\n        return fn(img, **params)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        fn = self.custom_apply_fns[\"mask\"]\n        return fn(mask, **params)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        is_ndarray = True\n\n        if not isinstance(bboxes, np.ndarray):\n            is_ndarray = False\n            bboxes = np.array(bboxes, dtype=np.float32)\n\n        fn = self.custom_apply_fns[\"bboxes\"]\n        result = fn(bboxes, **params)\n\n        if not is_ndarray:\n            return result.tolist()\n\n        return result\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        is_ndarray = True\n        if not isinstance(keypoints, np.ndarray):\n            is_ndarray = False\n            keypoints = np.array(keypoints, dtype=np.float32)\n\n        fn = self.custom_apply_fns[\"keypoints\"]\n        result = fn(keypoints, **params)\n\n        if not is_ndarray:\n            return result.tolist()\n\n        return result\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a Lambda transform serializable you should provide the `name` argument, \"\n                \"e.g. `Lambda(name='my_transform', image=<some func>, ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n\n    def __repr__(self) -> str:\n        state = {\"name\": self.name}\n        state.update(self.custom_apply_fns.items())  # type: ignore[arg-type]\n        state.update(self.get_base_init_args())\n        return f\"{self.__class__.__name__}({format_args(state)})\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.LaplaceParams","title":"class LaplaceParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class LaplaceParams(NoiseParamsBase):\n    noise_type: Literal[\"laplace\"] = \"laplace\"\n    mean_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=-1, max_val=1)),\n    ]\n    scale_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Morphological","title":"class Morphological (scale=(2, 3), operation='dilation', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply a morphological operation (dilation or erosion) to an image, with particular value for enhancing document scans.

Morphological operations modify the structure of the image. Dilation expands the white (foreground) regions in a binary or grayscale image, while erosion shrinks them. These operations are beneficial in document processing, for example: - Dilation helps in closing up gaps within text or making thin lines thicker, enhancing legibility for OCR (Optical Character Recognition). - Erosion can remove small white noise and detach connected objects, making the structure of larger objects more pronounced.

Parameters:

Name Type Description scale int or tuple/list of int

Specifies the size of the structuring element (kernel) used for the operation. - If an integer is provided, a square kernel of that size will be used. - If a tuple or list is provided, it should contain two integers representing the minimum and maximum sizes for the dilation kernel.

operation Literal[\"erosion\", \"dilation\"]

The morphological operation to apply. Default is 'dilation'.

p float

The probability of applying this transformation. Default is 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

https://github.com/facebookresearch/nougat

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n>>>     A.Morphological(scale=(2, 3), operation='dilation', p=0.5)\n>>> ])\n>>> image = transform(image=image)[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Morphological(DualTransform):\n    \"\"\"Apply a morphological operation (dilation or erosion) to an image,\n    with particular value for enhancing document scans.\n\n    Morphological operations modify the structure of the image.\n    Dilation expands the white (foreground) regions in a binary or grayscale image, while erosion shrinks them.\n    These operations are beneficial in document processing, for example:\n    - Dilation helps in closing up gaps within text or making thin lines thicker,\n        enhancing legibility for OCR (Optical Character Recognition).\n    - Erosion can remove small white noise and detach connected objects,\n        making the structure of larger objects more pronounced.\n\n    Args:\n        scale (int or tuple/list of int): Specifies the size of the structuring element (kernel) used for the operation.\n            - If an integer is provided, a square kernel of that size will be used.\n            - If a tuple or list is provided, it should contain two integers representing the minimum\n                and maximum sizes for the dilation kernel.\n        operation (Literal[\"erosion\", \"dilation\"]): The morphological operation to apply.\n            Default is 'dilation'.\n        p (float, optional): The probability of applying this transformation. Default is 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://github.com/facebookresearch/nougat\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        >>>     A.Morphological(scale=(2, 3), operation='dilation', p=0.5)\n        >>> ])\n        >>> image = transform(image=image)[\"image\"]\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: OnePlusIntRangeType\n        operation: MorphologyMode\n\n    def __init__(\n        self,\n        scale: ScaleIntType = (2, 3),\n        operation: MorphologyMode = \"dilation\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale = cast(tuple[int, int], scale)\n        self.operation = operation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.morphology(img, kernel, self.operation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"]\n\n        denormalized_boxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fmain.bboxes_morphology(\n            denormalized_boxes,\n            kernel,\n            self.operation,\n            image_shape,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return keypoints\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"kernel\": cv2.getStructuringElement(cv2.MORPH_ELLIPSE, self.scale),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"scale\", \"operation\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.MultiplicativeNoise","title":"class MultiplicativeNoise (multiplier=(0.9, 1.1), per_channel=False, elementwise=False, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply multiplicative noise to the input image.

This transform multiplies each pixel in the image by a random value or array of values, effectively creating a noise pattern that scales with the image intensity.

Parameters:

Name Type Description multiplier tuple[float, float]

The range for the random multiplier. Defines the range from which the multiplier is sampled. Default: (0.9, 1.1)

per_channel bool

If True, use a different random multiplier for each channel. If False, use the same multiplier for all channels. Setting this to False is slightly faster. Default: False

elementwise bool

If True, generates a unique multiplier for each pixel. If False, generates a single multiplier (or one per channel if per_channel=True). Default: False

p float

Probability of applying the transform. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • When elementwise=False and per_channel=False, a single multiplier is applied to the entire image.
  • When elementwise=False and per_channel=True, each channel gets a different multiplier.
  • When elementwise=True and per_channel=False, each pixel gets the same multiplier across all channels.
  • When elementwise=True and per_channel=True, each pixel in each channel gets a unique multiplier.
  • Setting per_channel=False is slightly faster, especially for larger images.
  • This transform can be used to simulate various lighting conditions or to create noise that scales with image intensity.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.MultiplicativeNoise(multiplier=(0.9, 1.1), per_channel=True, p=1.0)\n>>> result = transform(image=image)\n>>> noisy_image = result[\"image\"]\n

References

  • Multiplicative noise: https://en.wikipedia.org/wiki/Multiplicative_noise

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class MultiplicativeNoise(ImageOnlyTransform):\n    \"\"\"Apply multiplicative noise to the input image.\n\n    This transform multiplies each pixel in the image by a random value or array of values,\n    effectively creating a noise pattern that scales with the image intensity.\n\n    Args:\n        multiplier (tuple[float, float]): The range for the random multiplier.\n            Defines the range from which the multiplier is sampled.\n            Default: (0.9, 1.1)\n\n        per_channel (bool): If True, use a different random multiplier for each channel.\n            If False, use the same multiplier for all channels.\n            Setting this to False is slightly faster.\n            Default: False\n\n        elementwise (bool): If True, generates a unique multiplier for each pixel.\n            If False, generates a single multiplier (or one per channel if per_channel=True).\n            Default: False\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - When elementwise=False and per_channel=False, a single multiplier is applied to the entire image.\n        - When elementwise=False and per_channel=True, each channel gets a different multiplier.\n        - When elementwise=True and per_channel=False, each pixel gets the same multiplier across all channels.\n        - When elementwise=True and per_channel=True, each pixel in each channel gets a unique multiplier.\n        - Setting per_channel=False is slightly faster, especially for larger images.\n        - This transform can be used to simulate various lighting conditions or to create noise that\n          scales with image intensity.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.MultiplicativeNoise(multiplier=(0.9, 1.1), per_channel=True, p=1.0)\n        >>> result = transform(image=image)\n        >>> noisy_image = result[\"image\"]\n\n    References:\n        - Multiplicative noise: https://en.wikipedia.org/wiki/Multiplicative_noise\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        multiplier: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        per_channel: bool\n        elementwise: bool\n\n    def __init__(\n        self,\n        multiplier: ScaleFloatType = (0.9, 1.1),\n        per_channel: bool = False,\n        elementwise: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.multiplier = cast(tuple[float, float], multiplier)\n        self.elementwise = elementwise\n        self.per_channel = per_channel\n\n    def apply(\n        self,\n        img: np.ndarray,\n        multiplier: float | np.ndarray,\n        **kwargs: Any,\n    ) -> np.ndarray:\n        return multiply(img, multiplier)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if self.elementwise:\n            shape = image.shape if self.per_channel else (*image.shape[:2], 1)\n        else:\n            shape = (num_channels,) if self.per_channel else (1,)\n\n        multiplier = self.random_generator.uniform(\n            self.multiplier[0],\n            self.multiplier[1],\n            shape,\n        ).astype(np.float32)\n\n        if not self.per_channel and num_channels > 1:\n            # Replicate the multiplier for all channels if not per_channel\n            multiplier = np.repeat(multiplier, num_channels, axis=-1)\n\n        if not self.elementwise and self.per_channel:\n            # Reshape to broadcast correctly when not elementwise but per_channel\n            multiplier = multiplier.reshape(1, 1, -1)\n\n        if multiplier.shape != image.shape:\n            multiplier = multiplier.squeeze()\n\n        return {\"multiplier\": multiplier}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"multiplier\", \"elementwise\", \"per_channel\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.NoiseParamsBase","title":"class NoiseParamsBase ","text":"

Base class for all noise parameter models.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class NoiseParamsBase(BaseModel):\n    \"\"\"Base class for all noise parameter models.\"\"\"\n\n    model_config = ConfigDict(extra=\"forbid\")\n    noise_type: str\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Normalize","title":"class Normalize (mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), max_pixel_value=255.0, normalization='standard', always_apply=None, p=1.0) [view source on GitHub]","text":"

Applies various normalization techniques to an image. The specific normalization technique can be selected with the normalization parameter.

Standard normalization is applied using the formula: img = (img - mean * max_pixel_value) / (std * max_pixel_value). Other normalization techniques adjust the image based on global or per-channel statistics, or scale pixel values to a specified range.

Parameters:

Name Type Description mean ColorType | None

Mean values for standard normalization. For \"standard\" normalization, the default values are ImageNet mean values: (0.485, 0.456, 0.406).

std ColorType | None

Standard deviation values for standard normalization. For \"standard\" normalization, the default values are ImageNet standard deviation :(0.229, 0.224, 0.225).

max_pixel_value float | None

Maximum possible pixel value, used for scaling in standard normalization. Defaults to 255.0.

normalization Literal[\"standard\", \"image\", \"image_per_channel\", \"min_max\", \"min_max_per_channel\"]) Specifies the normalization technique to apply. Defaults to \"standard\". - \"standard\"

Applies the formula (img - mean * max_pixel_value) / (std * max_pixel_value). The default mean and std are based on ImageNet. You can use mean and std values of (0.5, 0.5, 0.5) for inception normalization. And mean values of (0, 0, 0) and std values of (1, 1, 1) for YOLO. - \"image\": Normalizes the whole image based on its global mean and standard deviation. - \"image_per_channel\": Normalizes the image per channel based on each channel's mean and standard deviation. - \"min_max\": Scales the image pixel values to a [0, 1] range based on the global minimum and maximum pixel values. - \"min_max_per_channel\": Scales each channel of the image pixel values to a [0, 1] range based on the per-channel minimum and maximum pixel values.

p float

Probability of applying the transform. Defaults to 1.0.

Targets

image

Image types: uint8, float32

Note

  • For \"standard\" normalization, mean, std, and max_pixel_value must be provided.
  • For other normalization types, these parameters are ignored.
  • For inception normalization, use mean values of (0.5, 0.5, 0.5).
  • For YOLO normalization, use mean values of (0, 0, 0) and std values of (1, 1, 1).
  • This transform is often used as a final step in image preprocessing pipelines to prepare images for neural network input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Standard ImageNet normalization\n>>> transform = A.Normalize(\n...     mean=(0.485, 0.456, 0.406),\n...     std=(0.229, 0.224, 0.225),\n...     max_pixel_value=255.0,\n...     p=1.0\n... )\n>>> normalized_image = transform(image=image)[\"image\"]\n>>>\n>>> # Min-max normalization\n>>> transform_minmax = A.Normalize(normalization=\"min_max\", p=1.0)\n>>> normalized_image_minmax = transform_minmax(image=image)[\"image\"]\n

References

  • ImageNet mean and std: https://pytorch.org/vision/stable/models.html
  • Inception preprocessing: https://keras.io/api/applications/inceptionv3/

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Normalize(ImageOnlyTransform):\n    \"\"\"Applies various normalization techniques to an image. The specific normalization technique can be selected\n        with the `normalization` parameter.\n\n    Standard normalization is applied using the formula:\n        `img = (img - mean * max_pixel_value) / (std * max_pixel_value)`.\n        Other normalization techniques adjust the image based on global or per-channel statistics,\n        or scale pixel values to a specified range.\n\n    Args:\n        mean (ColorType | None): Mean values for standard normalization.\n            For \"standard\" normalization, the default values are ImageNet mean values: (0.485, 0.456, 0.406).\n        std (ColorType | None): Standard deviation values for standard normalization.\n            For \"standard\" normalization, the default values are ImageNet standard deviation :(0.229, 0.224, 0.225).\n        max_pixel_value (float | None): Maximum possible pixel value, used for scaling in standard normalization.\n            Defaults to 255.0.\n        normalization (Literal[\"standard\", \"image\", \"image_per_channel\", \"min_max\", \"min_max_per_channel\"])\n            Specifies the normalization technique to apply. Defaults to \"standard\".\n            - \"standard\": Applies the formula `(img - mean * max_pixel_value) / (std * max_pixel_value)`.\n                The default mean and std are based on ImageNet. You can use mean and std values of (0.5, 0.5, 0.5)\n                for inception normalization. And mean values of (0, 0, 0) and std values of (1, 1, 1) for YOLO.\n            - \"image\": Normalizes the whole image based on its global mean and standard deviation.\n            - \"image_per_channel\": Normalizes the image per channel based on each channel's mean and standard deviation.\n            - \"min_max\": Scales the image pixel values to a [0, 1] range based on the global\n                minimum and maximum pixel values.\n            - \"min_max_per_channel\": Scales each channel of the image pixel values to a [0, 1]\n                range based on the per-channel minimum and maximum pixel values.\n\n        p (float): Probability of applying the transform. Defaults to 1.0.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - For \"standard\" normalization, `mean`, `std`, and `max_pixel_value` must be provided.\n        - For other normalization types, these parameters are ignored.\n        - For inception normalization, use mean values of (0.5, 0.5, 0.5).\n        - For YOLO normalization, use mean values of (0, 0, 0) and std values of (1, 1, 1).\n        - This transform is often used as a final step in image preprocessing pipelines to\n          prepare images for neural network input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Standard ImageNet normalization\n        >>> transform = A.Normalize(\n        ...     mean=(0.485, 0.456, 0.406),\n        ...     std=(0.229, 0.224, 0.225),\n        ...     max_pixel_value=255.0,\n        ...     p=1.0\n        ... )\n        >>> normalized_image = transform(image=image)[\"image\"]\n        >>>\n        >>> # Min-max normalization\n        >>> transform_minmax = A.Normalize(normalization=\"min_max\", p=1.0)\n        >>> normalized_image_minmax = transform_minmax(image=image)[\"image\"]\n\n    References:\n        - ImageNet mean and std: https://pytorch.org/vision/stable/models.html\n        - Inception preprocessing: https://keras.io/api/applications/inceptionv3/\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mean: ColorType | None\n        std: ColorType | None\n        max_pixel_value: float | None\n        normalization: Literal[\n            \"standard\",\n            \"image\",\n            \"image_per_channel\",\n            \"min_max\",\n            \"min_max_per_channel\",\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_normalization(self) -> Self:\n            if (\n                self.mean is None\n                or self.std is None\n                or (self.max_pixel_value is None and self.normalization == \"standard\")\n            ):\n                raise ValueError(\n                    \"mean, std, and max_pixel_value must be provided for standard normalization.\",\n                )\n            return self\n\n    def __init__(\n        self,\n        mean: ColorType | None = (0.485, 0.456, 0.406),\n        std: ColorType | None = (0.229, 0.224, 0.225),\n        max_pixel_value: float | None = 255.0,\n        normalization: Literal[\n            \"standard\",\n            \"image\",\n            \"image_per_channel\",\n            \"min_max\",\n            \"min_max_per_channel\",\n        ] = \"standard\",\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.mean = mean\n        self.mean_np = np.array(mean, dtype=np.float32) * max_pixel_value\n        self.std = std\n        self.denominator = np.reciprocal(\n            np.array(std, dtype=np.float32) * max_pixel_value,\n        )\n        self.max_pixel_value = max_pixel_value\n        self.normalization = normalization\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if self.normalization == \"standard\":\n            return normalize(\n                img,\n                self.mean_np,\n                self.denominator,\n            )\n        return normalize_per_image(img, self.normalization)\n\n    @batch_transform(\"channel\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(images, **params)\n\n    @batch_transform(\"channel\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(volume, **params)\n\n    @batch_transform(\"channel\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(volumes, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mean\", \"std\", \"max_pixel_value\", \"normalization\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PixelDropout","title":"class PixelDropout (dropout_prob=0.01, per_channel=False, drop_value=0, mask_drop_value=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Drops random pixels from the image.

This transform randomly sets pixels in the image to a specified value, effectively \"dropping out\" those pixels. It can be applied to both the image and its corresponding mask.

Parameters:

Name Type Description dropout_prob float

Probability of dropping out each pixel. Should be in the range [0, 1]. Default: 0.01

per_channel bool

If True, the dropout mask will be generated independently for each channel. If False, the same dropout mask will be applied to all channels. Default: False

drop_value float | Sequence[float] | None

Value to assign to the dropped pixels. If None, the value will be randomly sampled for each application: - For uint8 images: Random integer in [0, 255] - For float32 images: Random float in [0, 1] If a single number, that value will be used for all dropped pixels. If a sequence, it should contain one value per channel. Default: 0

mask_drop_value float | Sequence[float] | None

Value to assign to dropped pixels in the mask. If None, the mask will remain unchanged. If a single number, that value will be used for all dropped pixels in the mask. If a sequence, it should contain one value per channel of the mask. Note: Only applicable when per_channel=False. Default: None

always_apply bool

If True, the transform will always be applied. Default: False

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • When applied to bounding boxes, this transform may cause some boxes to have zero area if all pixels within the box are dropped. Such boxes will be removed.
  • When applied to keypoints, keypoints that fall on dropped pixels will be removed if the keypoint processor is configured to remove invisible keypoints.
  • The 'per_channel' option is not supported for mask dropout. If you need to drop pixels in a multi-channel mask independently, consider applying this transform multiple times with per_channel=False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> transform = A.PixelDropout(dropout_prob=0.1, per_channel=True, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> dropped_image, dropped_mask = result['image'], result['mask']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PixelDropout(DualTransform):\n    \"\"\"Drops random pixels from the image.\n\n    This transform randomly sets pixels in the image to a specified value, effectively \"dropping out\" those pixels.\n    It can be applied to both the image and its corresponding mask.\n\n    Args:\n        dropout_prob (float): Probability of dropping out each pixel. Should be in the range [0, 1].\n            Default: 0.01\n\n        per_channel (bool): If True, the dropout mask will be generated independently for each channel.\n            If False, the same dropout mask will be applied to all channels.\n            Default: False\n\n        drop_value (float | Sequence[float] | None): Value to assign to the dropped pixels.\n            If None, the value will be randomly sampled for each application:\n                - For uint8 images: Random integer in [0, 255]\n                - For float32 images: Random float in [0, 1]\n            If a single number, that value will be used for all dropped pixels.\n            If a sequence, it should contain one value per channel.\n            Default: 0\n\n        mask_drop_value (float | Sequence[float] | None): Value to assign to dropped pixels in the mask.\n            If None, the mask will remain unchanged.\n            If a single number, that value will be used for all dropped pixels in the mask.\n            If a sequence, it should contain one value per channel of the mask.\n            Note: Only applicable when per_channel=False.\n            Default: None\n\n        always_apply (bool): If True, the transform will always be applied.\n            Default: False\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - When applied to bounding boxes, this transform may cause some boxes to have zero area\n          if all pixels within the box are dropped. Such boxes will be removed.\n        - When applied to keypoints, keypoints that fall on dropped pixels will be removed if\n          the keypoint processor is configured to remove invisible keypoints.\n        - The 'per_channel' option is not supported for mask dropout. If you need to drop pixels\n          in a multi-channel mask independently, consider applying this transform multiple times\n          with per_channel=False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> transform = A.PixelDropout(dropout_prob=0.1, per_channel=True, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> dropped_image, dropped_mask = result['image'], result['mask']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        dropout_prob: ProbabilityType\n        per_channel: bool\n        drop_value: ScaleFloatType | None\n        mask_drop_value: ScaleFloatType | None\n\n        @model_validator(mode=\"after\")\n        def validate_mask_drop_value(self) -> Self:\n            if self.mask_drop_value is not None and self.per_channel:\n                msg = \"PixelDropout supports mask only with per_channel=False.\"\n                raise ValueError(msg)\n            return self\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        dropout_prob: float = 0.01,\n        per_channel: bool = False,\n        drop_value: ScaleFloatType | None = 0,\n        mask_drop_value: ScaleFloatType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.dropout_prob = dropout_prob\n        self.per_channel = per_channel\n        self.drop_value = drop_value\n        self.mask_drop_value = mask_drop_value\n\n    def apply(\n        self,\n        img: np.ndarray,\n        drop_mask: np.ndarray,\n        drop_value: float | Sequence[float],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.pixel_dropout(img, drop_mask, drop_value)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        drop_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        if self.mask_drop_value is None:\n            return mask\n\n        if mask.ndim == MONO_CHANNEL_DIMENSIONS:\n            drop_mask = np.squeeze(drop_mask)\n\n        return fmain.pixel_dropout(mask, drop_mask, self.mask_drop_value)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        drop_mask: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if drop_mask is None or self.per_channel:\n            return bboxes\n\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n\n        image_shape = params[\"shape\"][:2]\n\n        denormalized_bboxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fdropout.mask_dropout_bboxes(\n            denormalized_bboxes,\n            drop_mask,\n            image_shape,\n            processor.params.min_area,\n            processor.params.min_visibility,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        drop_mask: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if drop_mask is None or self.per_channel:\n            return keypoints\n\n        processor = cast(KeypointsProcessor, self.get_processor(\"keypoints\"))\n\n        if processor is None or not processor.params.remove_invisible:\n            return keypoints\n\n        return fdropout.mask_dropout_keypoints(keypoints, drop_mask)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        shape = image.shape if self.per_channel else image.shape[:2]\n\n        # Use choice to create boolean matrix, if we will use binomial after that we will need type conversion\n        drop_mask = self.random_generator.choice(\n            [True, False],\n            shape,\n            p=[self.dropout_prob, 1 - self.dropout_prob],\n        )\n\n        drop_value: float | Sequence[float] | np.ndarray\n\n        if drop_mask.ndim != image.ndim:\n            drop_mask = np.expand_dims(drop_mask, -1)\n        if self.drop_value is None:\n            drop_shape = 1 if is_grayscale_image(image) else int(image.shape[-1])\n\n            if image.dtype == np.uint8:\n                drop_value = self.random_generator.integers(\n                    0,\n                    int(MAX_VALUES_BY_DTYPE[image.dtype]),\n                    size=drop_shape,\n                    dtype=image.dtype,\n                )\n            elif image.dtype == np.float32:\n                drop_value = self.random_generator.uniform(\n                    0,\n                    1,\n                    size=drop_shape,\n                ).astype(image.dtype)\n            else:\n                raise ValueError(f\"Unsupported dtype: {image.dtype}\")\n        else:\n            drop_value = self.drop_value\n\n        return {\"drop_mask\": drop_mask, \"drop_value\": drop_value}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return (\"dropout_prob\", \"per_channel\", \"drop_value\", \"mask_drop_value\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlanckianJitter","title":"class PlanckianJitter (mode='blackbody', temperature_limit=None, sampling_method='uniform', p=0.5, always_apply=None) [view source on GitHub]","text":"

Applies Planckian Jitter to the input image, simulating color temperature variations in illumination.

This transform adjusts the color of an image to mimic the effect of different color temperatures of light sources, based on Planck's law of black body radiation. It can simulate the appearance of an image under various lighting conditions, from warm (reddish) to cool (bluish) color casts.

PlanckianJitter vs. ColorJitter: PlanckianJitter is fundamentally different from ColorJitter in its approach and use cases: 1. Physics-based: PlanckianJitter is grounded in the physics of light, simulating real-world color temperature changes. ColorJitter applies arbitrary color adjustments. 2. Natural effects: This transform produces color shifts that correspond to natural lighting variations, making it ideal for outdoor scene simulation or color constancy problems. 3. Single parameter: Color changes are controlled by a single, physically meaningful parameter (color temperature), unlike ColorJitter's multiple abstract parameters. 4. Correlated changes: Color shifts are correlated across channels in a way that mimics natural light, whereas ColorJitter can make independent channel adjustments.

When to use PlanckianJitter: - Simulating different times of day or lighting conditions in outdoor scenes - Augmenting data for computer vision tasks that need to be robust to natural lighting changes - Preparing synthetic data to better match real-world lighting variations - Color constancy research or applications - When you need physically plausible color variations rather than arbitrary color changes

The logic behind PlanckianJitter: As the color temperature increases: 1. Lower temperatures (around 3000K) produce warm, reddish tones, simulating sunset or incandescent lighting. 2. Mid-range temperatures (around 5500K) correspond to daylight. 3. Higher temperatures (above 7000K) result in cool, bluish tones, similar to overcast sky or shade. This progression mimics the natural variation of sunlight throughout the day and in different weather conditions.

Parameters:

Name Type Description mode Literal[\"blackbody\", \"cied\"]

The mode of the transformation. - \"blackbody\": Simulates blackbody radiation color changes. - \"cied\": Uses the CIE D illuminant series for color temperature simulation. Default: \"blackbody\"

temperature_limit tuple[int, int] | None

The range of color temperatures (in Kelvin) to sample from. - For \"blackbody\" mode: Should be within [3000K, 15000K]. Default: (3000, 15000) - For \"cied\" mode: Should be within [4000K, 15000K]. Default: (4000, 15000) If None, the default ranges will be used based on the selected mode. Higher temperatures produce cooler (bluish) images, lower temperatures produce warmer (reddish) images.

sampling_method Literal[\"uniform\", \"gaussian\"]

Method to sample the temperature. - \"uniform\": Samples uniformly across the specified range. - \"gaussian\": Samples from a Gaussian distribution centered at 6500K (approximate daylight). Default: \"uniform\"

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The transform preserves the overall brightness of the image while shifting its color.
  • The \"blackbody\" mode provides a wider range of color shifts, especially in the lower (warmer) temperatures.
  • The \"cied\" mode is based on standard illuminants and may provide more realistic daylight variations.
  • The Gaussian sampling method tends to produce more subtle variations, as it's centered around daylight.
  • Unlike ColorJitter, this transform ensures that color changes are physically plausible and correlated across channels, maintaining the natural appearance of the scene under different lighting conditions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PlanckianJitter(mode=\"blackbody\",\n...                               temperature_range=(3000, 9000),\n...                               sampling_method=\"uniform\",\n...                               p=1.0)\n>>> result = transform(image=image)\n>>> jittered_image = result[\"image\"]\n

References

  • Planck's law: https://en.wikipedia.org/wiki/Planck%27s_law
  • CIE Standard Illuminants: https://en.wikipedia.org/wiki/Standard_illuminant
  • Color temperature: https://en.wikipedia.org/wiki/Color_temperature
  • Implementation inspired by: https://github.com/TheZino/PlanckianJitter

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlanckianJitter(ImageOnlyTransform):\n    \"\"\"Applies Planckian Jitter to the input image, simulating color temperature variations in illumination.\n\n    This transform adjusts the color of an image to mimic the effect of different color temperatures\n    of light sources, based on Planck's law of black body radiation. It can simulate the appearance\n    of an image under various lighting conditions, from warm (reddish) to cool (bluish) color casts.\n\n    PlanckianJitter vs. ColorJitter:\n    PlanckianJitter is fundamentally different from ColorJitter in its approach and use cases:\n    1. Physics-based: PlanckianJitter is grounded in the physics of light, simulating real-world\n       color temperature changes. ColorJitter applies arbitrary color adjustments.\n    2. Natural effects: This transform produces color shifts that correspond to natural lighting\n       variations, making it ideal for outdoor scene simulation or color constancy problems.\n    3. Single parameter: Color changes are controlled by a single, physically meaningful parameter\n       (color temperature), unlike ColorJitter's multiple abstract parameters.\n    4. Correlated changes: Color shifts are correlated across channels in a way that mimics natural\n       light, whereas ColorJitter can make independent channel adjustments.\n\n    When to use PlanckianJitter:\n    - Simulating different times of day or lighting conditions in outdoor scenes\n    - Augmenting data for computer vision tasks that need to be robust to natural lighting changes\n    - Preparing synthetic data to better match real-world lighting variations\n    - Color constancy research or applications\n    - When you need physically plausible color variations rather than arbitrary color changes\n\n    The logic behind PlanckianJitter:\n    As the color temperature increases:\n    1. Lower temperatures (around 3000K) produce warm, reddish tones, simulating sunset or incandescent lighting.\n    2. Mid-range temperatures (around 5500K) correspond to daylight.\n    3. Higher temperatures (above 7000K) result in cool, bluish tones, similar to overcast sky or shade.\n    This progression mimics the natural variation of sunlight throughout the day and in different weather conditions.\n\n    Args:\n        mode (Literal[\"blackbody\", \"cied\"]): The mode of the transformation.\n            - \"blackbody\": Simulates blackbody radiation color changes.\n            - \"cied\": Uses the CIE D illuminant series for color temperature simulation.\n            Default: \"blackbody\"\n\n        temperature_limit (tuple[int, int] | None): The range of color temperatures (in Kelvin) to sample from.\n            - For \"blackbody\" mode: Should be within [3000K, 15000K]. Default: (3000, 15000)\n            - For \"cied\" mode: Should be within [4000K, 15000K]. Default: (4000, 15000)\n            If None, the default ranges will be used based on the selected mode.\n            Higher temperatures produce cooler (bluish) images, lower temperatures produce warmer (reddish) images.\n\n        sampling_method (Literal[\"uniform\", \"gaussian\"]): Method to sample the temperature.\n            - \"uniform\": Samples uniformly across the specified range.\n            - \"gaussian\": Samples from a Gaussian distribution centered at 6500K (approximate daylight).\n            Default: \"uniform\"\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The transform preserves the overall brightness of the image while shifting its color.\n        - The \"blackbody\" mode provides a wider range of color shifts, especially in the lower (warmer) temperatures.\n        - The \"cied\" mode is based on standard illuminants and may provide more realistic daylight variations.\n        - The Gaussian sampling method tends to produce more subtle variations, as it's centered around daylight.\n        - Unlike ColorJitter, this transform ensures that color changes are physically plausible and correlated\n          across channels, maintaining the natural appearance of the scene under different lighting conditions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PlanckianJitter(mode=\"blackbody\",\n        ...                               temperature_range=(3000, 9000),\n        ...                               sampling_method=\"uniform\",\n        ...                               p=1.0)\n        >>> result = transform(image=image)\n        >>> jittered_image = result[\"image\"]\n\n    References:\n        - Planck's law: https://en.wikipedia.org/wiki/Planck%27s_law\n        - CIE Standard Illuminants: https://en.wikipedia.org/wiki/Standard_illuminant\n        - Color temperature: https://en.wikipedia.org/wiki/Color_temperature\n        - Implementation inspired by: https://github.com/TheZino/PlanckianJitter\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: Literal[\"blackbody\", \"cied\"]\n        temperature_limit: Annotated[tuple[int, int], AfterValidator(nondecreasing)] | None\n        sampling_method: Literal[\"uniform\", \"gaussian\"]\n\n        @model_validator(mode=\"after\")\n        def validate_temperature(self) -> Self:\n            max_temp = int(PLANKIAN_JITTER_CONST[\"MAX_TEMP\"])\n\n            if self.temperature_limit is None:\n                if self.mode == \"blackbody\":\n                    self.temperature_limit = (\n                        int(PLANKIAN_JITTER_CONST[\"MIN_BLACKBODY_TEMP\"]),\n                        max_temp,\n                    )\n                elif self.mode == \"cied\":\n                    self.temperature_limit = (\n                        int(PLANKIAN_JITTER_CONST[\"MIN_CIED_TEMP\"]),\n                        max_temp,\n                    )\n            else:\n                if self.mode == \"blackbody\" and (\n                    min(self.temperature_limit) < PLANKIAN_JITTER_CONST[\"MIN_BLACKBODY_TEMP\"]\n                    or max(self.temperature_limit) > max_temp\n                ):\n                    raise ValueError(\n                        \"Temperature limits for blackbody should be in [3000, 15000] range\",\n                    )\n                if self.mode == \"cied\" and (\n                    min(self.temperature_limit) < PLANKIAN_JITTER_CONST[\"MIN_CIED_TEMP\"]\n                    or max(self.temperature_limit) > max_temp\n                ):\n                    raise ValueError(\n                        \"Temperature limits for CIED should be in [4000, 15000] range\",\n                    )\n\n                if not self.temperature_limit[0] <= PLANKIAN_JITTER_CONST[\"WHITE_TEMP\"] <= self.temperature_limit[1]:\n                    raise ValueError(\n                        \"White temperature should be within the temperature limits\",\n                    )\n\n            return self\n\n    def __init__(\n        self,\n        mode: Literal[\"blackbody\", \"cied\"] = \"blackbody\",\n        temperature_limit: tuple[int, int] | None = None,\n        sampling_method: Literal[\"uniform\", \"gaussian\"] = \"uniform\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ) -> None:\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.mode = mode\n        self.temperature_limit = cast(tuple[int, int], temperature_limit)\n        self.sampling_method = sampling_method\n\n    def apply(self, img: np.ndarray, temperature: int, **params: Any) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.planckian_jitter(img, temperature, mode=self.mode)\n\n    def get_params(self) -> dict[str, Any]:\n        sampling_prob_boundary = PLANKIAN_JITTER_CONST[\"SAMPLING_TEMP_PROB\"]\n        sampling_temp_boundary = PLANKIAN_JITTER_CONST[\"WHITE_TEMP\"]\n\n        if self.sampling_method == \"uniform\":\n            # Split into 2 cases to avoid selecting cold temperatures (>6000) too often\n            if self.py_random.random() < sampling_prob_boundary:\n                temperature = self.py_random.uniform(\n                    self.temperature_limit[0],\n                    sampling_temp_boundary,\n                )\n            else:\n                temperature = self.py_random.uniform(\n                    sampling_temp_boundary,\n                    self.temperature_limit[1],\n                )\n        elif self.sampling_method == \"gaussian\":\n            # Sample values from asymmetric gaussian distribution\n            if self.py_random.random() < sampling_prob_boundary:\n                # Left side\n                shift = np.abs(\n                    self.py_random.gauss(\n                        0,\n                        np.abs(sampling_temp_boundary - self.temperature_limit[0]) / 3,\n                    ),\n                )\n                temperature = sampling_temp_boundary - shift\n            else:\n                # Right side\n                shift = np.abs(\n                    self.py_random.gauss(\n                        0,\n                        np.abs(self.temperature_limit[1] - sampling_temp_boundary) / 3,\n                    ),\n                )\n                temperature = sampling_temp_boundary + shift\n        else:\n            raise ValueError(f\"Unknown sampling method: {self.sampling_method}\")\n\n        # Ensure temperature is within the valid range\n        temperature = np.clip(\n            temperature,\n            self.temperature_limit[0],\n            self.temperature_limit[1],\n        )\n\n        return {\"temperature\": int(temperature)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mode\", \"temperature_limit\", \"sampling_method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlasmaBrightnessContrast","title":"class PlasmaBrightnessContrast (brightness_range=(-0.3, 0.3), contrast_range=(-0.3, 0.3), plasma_size=256, roughness=3.0, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply plasma fractal pattern to modify image brightness and contrast.

This transform uses the Diamond-Square algorithm to generate organic-looking fractal patterns that are then used to create spatially-varying brightness and contrast adjustments. The result is a natural-looking, non-uniform modification of the image.

Parameters:

Name Type Description brightness_range float, float

Range for brightness adjustment strength. Values between -1 and 1: - Positive values increase brightness - Negative values decrease brightness - 0 means no brightness change Default: (-0.3, 0.3)

contrast_range float, float

Range for contrast adjustment strength. Values between -1 and 1: - Positive values increase contrast - Negative values decrease contrast - 0 means no contrast change Default: (-0.3, 0.3)

plasma_size int

Size of the plasma pattern. Will be rounded up to nearest power of 2. Larger values create more detailed patterns. Default: 256

roughness float

Controls the roughness of the plasma pattern. Higher values create more rough/sharp transitions. Must be greater than 0. Typical values are between 1.0 and 5.0. Default: 3.0

p (float): Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Mathematical Formulation: 1. Plasma Pattern Generation: The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] by: - Starting with random corner values - Recursively computing midpoints using: M = (V1 + V2 + V3 + V4)/4 + R(d) where V1..V4 are corner values and R(d) is random noise that decreases with distance d according to the roughness parameter.

2. Brightness Adjustment:\n   For each pixel (x,y):\n   O(x,y) = I(x,y) + b\u00b7P(x,y)\u00b7max_value\n   where:\n   - I is the input image\n   - b is the brightness factor\n   - P is the plasma pattern\n   - max_value is the maximum possible pixel value\n\n3. Contrast Adjustment:\n   For each pixel (x,y):\n   O(x,y) = \u03bc + (I(x,y) - \u03bc)\u00b7(1 + c\u00b7P(x,y))\n   where:\n   - \u03bc is the mean pixel value\n   - c is the contrast factor\n   - P is the plasma pattern\n

Note

  • The plasma pattern creates smooth, organic variations in the adjustments
  • Brightness and contrast modifications are applied sequentially
  • Final values are clipped to valid range [0, max_value]
  • The same plasma pattern is used for both brightness and contrast to maintain coherent spatial variations

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-parameters","title":"Default parameters","text":"Python
>>> transform = A.PlasmaBrightnessContrast(p=1.0)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-adjustments-with-fine-pattern","title":"Custom adjustments with fine pattern","text":"Python
>>> transform = A.PlasmaBrightnessContrast(\n...     brightness_range=(-0.5, 0.5),\n...     contrast_range=(-0.3, 0.3),\n...     plasma_size=512,  # More detailed pattern\n...     roughness=2.5,    # Smoother transitions\n...     p=1.0\n... )\n

References

.. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\" Communications of the ACM, 1982. Paper introducing the Diamond-Square algorithm.

.. [2] Miller, \"The Diamond-Square Algorithm: A Detailed Analysis,\" Journal of Computer Graphics Techniques, 2016. Comprehensive analysis of the algorithm and its properties.

.. [3] Ebert et al., \"Texturing & Modeling: A Procedural Approach,\" Chapter 12: Noise, Hypertexture, Antialiasing, and Gesture. Detailed coverage of procedural noise patterns.

.. [4] Diamond-Square algorithm: https://en.wikipedia.org/wiki/Diamond-square_algorithm

.. [5] Plasma effect: https://lodev.org/cgtutor/plasma.html

See Also: - RandomBrightnessContrast: For uniform brightness/contrast adjustments - CLAHE: For contrast limited adaptive histogram equalization - FancyPCA: For color-based contrast enhancement - HistogramMatching: For reference-based contrast adjustment

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlasmaBrightnessContrast(ImageOnlyTransform):\n    \"\"\"Apply plasma fractal pattern to modify image brightness and contrast.\n\n    This transform uses the Diamond-Square algorithm to generate organic-looking fractal patterns\n    that are then used to create spatially-varying brightness and contrast adjustments.\n    The result is a natural-looking, non-uniform modification of the image.\n\n    Args:\n        brightness_range ((float, float)): Range for brightness adjustment strength.\n            Values between -1 and 1:\n            - Positive values increase brightness\n            - Negative values decrease brightness\n            - 0 means no brightness change\n            Default: (-0.3, 0.3)\n\n        contrast_range ((float, float)): Range for contrast adjustment strength.\n            Values between -1 and 1:\n            - Positive values increase contrast\n            - Negative values decrease contrast\n            - 0 means no contrast change\n            Default: (-0.3, 0.3)\n\n        plasma_size (int): Size of the plasma pattern. Will be rounded up to nearest power of 2.\n            Larger values create more detailed patterns. Default: 256\n\n        roughness (float): Controls the roughness of the plasma pattern.\n            Higher values create more rough/sharp transitions.\n            Must be greater than 0.\n            Typical values are between 1.0 and 5.0. Default: 3.0\n\n            p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Mathematical Formulation:\n        1. Plasma Pattern Generation:\n           The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] by:\n           - Starting with random corner values\n           - Recursively computing midpoints using:\n             M = (V1 + V2 + V3 + V4)/4 + R(d)\n           where V1..V4 are corner values and R(d) is random noise that\n           decreases with distance d according to the roughness parameter.\n\n        2. Brightness Adjustment:\n           For each pixel (x,y):\n           O(x,y) = I(x,y) + b\u00b7P(x,y)\u00b7max_value\n           where:\n           - I is the input image\n           - b is the brightness factor\n           - P is the plasma pattern\n           - max_value is the maximum possible pixel value\n\n        3. Contrast Adjustment:\n           For each pixel (x,y):\n           O(x,y) = \u03bc + (I(x,y) - \u03bc)\u00b7(1 + c\u00b7P(x,y))\n           where:\n           - \u03bc is the mean pixel value\n           - c is the contrast factor\n           - P is the plasma pattern\n\n    Note:\n        - The plasma pattern creates smooth, organic variations in the adjustments\n        - Brightness and contrast modifications are applied sequentially\n        - Final values are clipped to valid range [0, max_value]\n        - The same plasma pattern is used for both brightness and contrast\n          to maintain coherent spatial variations\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Default parameters\n        >>> transform = A.PlasmaBrightnessContrast(p=1.0)\n\n        # Custom adjustments with fine pattern\n        >>> transform = A.PlasmaBrightnessContrast(\n        ...     brightness_range=(-0.5, 0.5),\n        ...     contrast_range=(-0.3, 0.3),\n        ...     plasma_size=512,  # More detailed pattern\n        ...     roughness=2.5,    # Smoother transitions\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\"\n               Communications of the ACM, 1982.\n               Paper introducing the Diamond-Square algorithm.\n\n        .. [2] Miller, \"The Diamond-Square Algorithm: A Detailed Analysis,\"\n               Journal of Computer Graphics Techniques, 2016.\n               Comprehensive analysis of the algorithm and its properties.\n\n        .. [3] Ebert et al., \"Texturing & Modeling: A Procedural Approach,\"\n               Chapter 12: Noise, Hypertexture, Antialiasing, and Gesture.\n               Detailed coverage of procedural noise patterns.\n\n        .. [4] Diamond-Square algorithm:\n               https://en.wikipedia.org/wiki/Diamond-square_algorithm\n\n        .. [5] Plasma effect:\n               https://lodev.org/cgtutor/plasma.html\n\n    See Also:\n        - RandomBrightnessContrast: For uniform brightness/contrast adjustments\n        - CLAHE: For contrast limited adaptive histogram equalization\n        - FancyPCA: For color-based contrast enhancement\n        - HistogramMatching: For reference-based contrast adjustment\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n        ]\n        contrast_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n        ]\n        plasma_size: int = Field(default=256, gt=0)\n        roughness: float = Field(default=3.0, gt=0)\n\n    def __init__(\n        self,\n        brightness_range: tuple[float, float] = (-0.3, 0.3),\n        contrast_range: tuple[float, float] = (-0.3, 0.3),\n        plasma_size: int = 256,\n        roughness: float = 3.0,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.brightness_range = brightness_range\n        self.contrast_range = contrast_range\n        self.plasma_size = plasma_size\n        self.roughness = roughness\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample adjustment strengths\n        brightness = self.py_random.uniform(*self.brightness_range)\n        contrast = self.py_random.uniform(*self.contrast_range)\n\n        # Generate plasma pattern\n        plasma = fmain.generate_plasma_pattern(\n            target_shape=image.shape[:2],\n            size=self.plasma_size,\n            roughness=self.roughness,\n            random_generator=self.random_generator,\n        )\n\n        return {\n            \"brightness_factor\": brightness,\n            \"contrast_factor\": contrast,\n            \"plasma_pattern\": plasma,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        brightness_factor: float,\n        contrast_factor: float,\n        plasma_pattern: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_plasma_brightness_contrast(\n            img,\n            brightness_factor,\n            contrast_factor,\n            plasma_pattern,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"brightness_range\", \"contrast_range\", \"plasma_size\", \"roughness\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlasmaShadow","title":"class PlasmaShadow (shadow_intensity_range=(0.3, 0.7), plasma_size=256, roughness=3.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply plasma-based shadow effect to the image.

Creates organic-looking shadows using plasma fractal noise pattern. The shadow intensity varies smoothly across the image, creating natural-looking darkening effects that can simulate shadows, shading, or lighting variations.

Parameters:

Name Type Description shadow_intensity_range tuple[float, float]

Range for shadow intensity. Values between 0 and 1: - 0 means no shadow (original image) - 1 means maximum darkening (black) - Values between create partial shadows Default: (0.3, 0.7)

plasma_size int

Size of the plasma pattern. Will be rounded up to nearest power of 2. Larger values create more detailed shadow patterns: - Small values (~64): Large, smooth shadow regions - Medium values (~256): Balanced detail level - Large values (~512+): Fine shadow details Default: 256

roughness float

Controls the roughness of the plasma pattern. Higher values create more rough/sharp shadow transitions. Must be greater than 0: - Low values (~1.0): Very smooth transitions - Medium values (~3.0): Natural-looking shadows - High values (~5.0): More dramatic, sharp shadows Default: 3.0

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • The transform darkens the image using a plasma pattern
  • Works with any number of channels (grayscale, RGB, multispectral)
  • Shadow pattern is generated using Diamond-Square algorithm
  • The same shadow pattern is applied to all channels
  • Final values are clipped to valid range [0, max_value]

Mathematical Formulation: 1. Plasma Pattern Generation: The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] with fractal characteristics controlled by roughness parameter.

2. Shadow Application:\n   For each pixel (x,y):\n   O(x,y) = I(x,y) * (1 - i\u00b7P(x,y))\n   where:\n   - I is the input image\n   - P is the plasma pattern\n   - i is the shadow intensity\n   - O is the output image\n

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-parameters-for-natural-shadows","title":"Default parameters for natural shadows","text":"Python
>>> transform = A.PlasmaShadow(p=1.0)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--subtle-smooth-shadows","title":"Subtle, smooth shadows","text":"Python
>>> transform = A.PlasmaShadow(\n...     shadow_intensity=(0.1, 0.3),\n...     plasma_size=128,\n...     roughness=1.5,\n...     p=1.0\n... )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--dramatic-detailed-shadows","title":"Dramatic, detailed shadows","text":"Python
>>> transform = A.PlasmaShadow(\n...     shadow_intensity=(0.5, 0.9),\n...     plasma_size=512,\n...     roughness=4.0,\n...     p=1.0\n... )\n

References

.. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\" Communications of the ACM, 1982. Paper introducing the Diamond-Square algorithm.

.. [2] Diamond-Square algorithm: https://en.wikipedia.org/wiki/Diamond-square_algorithm

See Also: - PlasmaBrightnessContrast: For brightness/contrast adjustments using plasma patterns - RandomShadow: For geometric shadow effects - RandomToneCurve: For global lighting adjustments

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlasmaShadow(ImageOnlyTransform):\n    \"\"\"Apply plasma-based shadow effect to the image.\n\n    Creates organic-looking shadows using plasma fractal noise pattern.\n    The shadow intensity varies smoothly across the image, creating natural-looking\n    darkening effects that can simulate shadows, shading, or lighting variations.\n\n    Args:\n        shadow_intensity_range (tuple[float, float]): Range for shadow intensity.\n            Values between 0 and 1:\n            - 0 means no shadow (original image)\n            - 1 means maximum darkening (black)\n            - Values between create partial shadows\n            Default: (0.3, 0.7)\n\n        plasma_size (int): Size of the plasma pattern. Will be rounded up to nearest power of 2.\n            Larger values create more detailed shadow patterns:\n            - Small values (~64): Large, smooth shadow regions\n            - Medium values (~256): Balanced detail level\n            - Large values (~512+): Fine shadow details\n            Default: 256\n\n        roughness (float): Controls the roughness of the plasma pattern.\n            Higher values create more rough/sharp shadow transitions.\n            Must be greater than 0:\n            - Low values (~1.0): Very smooth transitions\n            - Medium values (~3.0): Natural-looking shadows\n            - High values (~5.0): More dramatic, sharp shadows\n            Default: 3.0\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform darkens the image using a plasma pattern\n        - Works with any number of channels (grayscale, RGB, multispectral)\n        - Shadow pattern is generated using Diamond-Square algorithm\n        - The same shadow pattern is applied to all channels\n        - Final values are clipped to valid range [0, max_value]\n\n    Mathematical Formulation:\n        1. Plasma Pattern Generation:\n           The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1]\n           with fractal characteristics controlled by roughness parameter.\n\n        2. Shadow Application:\n           For each pixel (x,y):\n           O(x,y) = I(x,y) * (1 - i\u00b7P(x,y))\n           where:\n           - I is the input image\n           - P is the plasma pattern\n           - i is the shadow intensity\n           - O is the output image\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Default parameters for natural shadows\n        >>> transform = A.PlasmaShadow(p=1.0)\n\n        # Subtle, smooth shadows\n        >>> transform = A.PlasmaShadow(\n        ...     shadow_intensity=(0.1, 0.3),\n        ...     plasma_size=128,\n        ...     roughness=1.5,\n        ...     p=1.0\n        ... )\n\n        # Dramatic, detailed shadows\n        >>> transform = A.PlasmaShadow(\n        ...     shadow_intensity=(0.5, 0.9),\n        ...     plasma_size=512,\n        ...     roughness=4.0,\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\"\n               Communications of the ACM, 1982.\n               Paper introducing the Diamond-Square algorithm.\n\n        .. [2] Diamond-Square algorithm:\n               https://en.wikipedia.org/wiki/Diamond-square_algorithm\n\n    See Also:\n        - PlasmaBrightnessContrast: For brightness/contrast adjustments using plasma patterns\n        - RandomShadow: For geometric shadow effects\n        - RandomToneCurve: For global lighting adjustments\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        shadow_intensity_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        plasma_size: int = Field(default=256, gt=0)\n        roughness: float = Field(default=3.0, gt=0)\n\n    def __init__(\n        self,\n        shadow_intensity_range: tuple[float, float] = (0.3, 0.7),\n        plasma_size: int = 256,\n        roughness: float = 3.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.shadow_intensity_range = shadow_intensity_range\n        self.plasma_size = plasma_size\n        self.roughness = roughness\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample shadow intensity\n        intensity = self.py_random.uniform(*self.shadow_intensity_range)\n\n        # Generate plasma pattern\n        plasma = fmain.generate_plasma_pattern(\n            target_shape=image.shape[:2],\n            size=self.plasma_size,\n            roughness=self.roughness,\n            random_generator=self.random_generator,\n        )\n\n        return {\n            \"intensity\": intensity,\n            \"plasma_pattern\": plasma,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        intensity: float,\n        plasma_pattern: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_plasma_shadow(img, intensity, plasma_pattern)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"shadow_intensity_range\", \"plasma_size\", \"roughness\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Posterize","title":"class Posterize (num_bits=4, p=0.5, always_apply=None) [view source on GitHub]","text":"

Reduces the number of bits for each color channel in the image.

This transform applies color posterization, a technique that reduces the number of distinct colors used in an image. It works by lowering the number of bits used to represent each color channel, effectively creating a \"poster-like\" effect with fewer color gradations.

Parameters:

Name Type Description num_bits int | tuple[int, int] | list[int] | list[tuple[int, int]]

Defines the number of bits to keep for each color channel. Can be specified in several ways: - Single int: Same number of bits for all channels. Range: [1, 7]. - tuple of two ints: (min_bits, max_bits) to randomly choose from. Range for each: [1, 7]. - list of three ints: Specific number of bits for each channel [r_bits, g_bits, b_bits]. - list of three tuples: Ranges for each channel [(r_min, r_max), (g_min, g_max), (b_min, b_max)]. Default: 4

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effect becomes more pronounced as the number of bits is reduced.
  • This transform can create interesting artistic effects or be used for image compression simulation.
  • Posterization is particularly useful for:
  • Creating stylized or retro-looking images
  • Reducing the color palette for specific artistic effects
  • Simulating the look of older or lower-quality digital images
  • Data augmentation in scenarios where color depth might vary

Mathematical Background: For an 8-bit color channel, posterization to n bits can be expressed as: new_value = (old_value >> (8 - n)) << (8 - n) This operation keeps the n most significant bits and sets the rest to zero.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--posterize-all-channels-to-3-bits","title":"Posterize all channels to 3 bits","text":"Python
>>> transform = A.Posterize(num_bits=3, p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--randomly-posterize-between-2-and-5-bits","title":"Randomly posterize between 2 and 5 bits","text":"Python
>>> transform = A.Posterize(num_bits=(2, 5), p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--different-bits-for-each-channel","title":"Different bits for each channel","text":"Python
>>> transform = A.Posterize(num_bits=[3, 5, 2], p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--range-of-bits-for-each-channel","title":"Range of bits for each channel","text":"Python
>>> transform = A.Posterize(num_bits=[(1, 3), (3, 5), (2, 4)], p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n

References

  • Color Quantization: https://en.wikipedia.org/wiki/Color_quantization
  • Posterization: https://en.wikipedia.org/wiki/Posterization

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Posterize(ImageOnlyTransform):\n    \"\"\"Reduces the number of bits for each color channel in the image.\n\n    This transform applies color posterization, a technique that reduces the number of distinct\n    colors used in an image. It works by lowering the number of bits used to represent each\n    color channel, effectively creating a \"poster-like\" effect with fewer color gradations.\n\n    Args:\n        num_bits (int | tuple[int, int] | list[int] | list[tuple[int, int]]):\n            Defines the number of bits to keep for each color channel. Can be specified in several ways:\n            - Single int: Same number of bits for all channels. Range: [1, 7].\n            - tuple of two ints: (min_bits, max_bits) to randomly choose from. Range for each: [1, 7].\n            - list of three ints: Specific number of bits for each channel [r_bits, g_bits, b_bits].\n            - list of three tuples: Ranges for each channel [(r_min, r_max), (g_min, g_max), (b_min, b_max)].\n            Default: 4\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effect becomes more pronounced as the number of bits is reduced.\n        - This transform can create interesting artistic effects or be used for image compression simulation.\n        - Posterization is particularly useful for:\n          * Creating stylized or retro-looking images\n          * Reducing the color palette for specific artistic effects\n          * Simulating the look of older or lower-quality digital images\n          * Data augmentation in scenarios where color depth might vary\n\n    Mathematical Background:\n        For an 8-bit color channel, posterization to n bits can be expressed as:\n        new_value = (old_value >> (8 - n)) << (8 - n)\n        This operation keeps the n most significant bits and sets the rest to zero.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Posterize all channels to 3 bits\n        >>> transform = A.Posterize(num_bits=3, p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Randomly posterize between 2 and 5 bits\n        >>> transform = A.Posterize(num_bits=(2, 5), p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Different bits for each channel\n        >>> transform = A.Posterize(num_bits=[3, 5, 2], p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Range of bits for each channel\n        >>> transform = A.Posterize(num_bits=[(1, 3), (3, 5), (2, 4)], p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n    References:\n        - Color Quantization: https://en.wikipedia.org/wiki/Color_quantization\n        - Posterization: https://en.wikipedia.org/wiki/Posterization\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_bits: int | tuple[int, int] | list[tuple[int, int]]\n\n        @field_validator(\"num_bits\")\n        @classmethod\n        def validate_num_bits(\n            cls,\n            num_bits: Any,\n        ) -> tuple[int, int] | list[tuple[int, int]]:\n            if isinstance(num_bits, int):\n                if num_bits < 1 or num_bits > SEVEN:\n                    raise ValueError(\"num_bits must be in the range [1, 7]\")\n                return (num_bits, num_bits)\n            if isinstance(num_bits, Sequence) and len(num_bits) > PAIR:\n                return [to_tuple(i, i) for i in num_bits]\n            return cast(tuple[int, int], to_tuple(num_bits, num_bits))\n\n    def __init__(\n        self,\n        num_bits: int | tuple[int, int] | list[tuple[int, int]] = 4,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_bits = cast(Union[tuple[int, int], list[tuple[int, int]]], num_bits)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        num_bits: Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.posterize(img, num_bits)\n\n    def get_params(self) -> dict[str, Any]:\n        if isinstance(self.num_bits, list):\n            num_bits = [self.py_random.randint(*i) for i in self.num_bits]\n            return {\"num_bits\": num_bits}\n        return {\"num_bits\": self.py_random.randint(*self.num_bits)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"num_bits\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RGBShift","title":"class RGBShift (r_shift_limit=(-20, 20), g_shift_limit=(-20, 20), b_shift_limit=(-20, 20), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shift values for each channel of the input RGB image.

A specialized version of AdditiveNoise that applies constant uniform shifts to RGB channels. Each channel (R,G,B) can have its own shift range specified.

Parameters:

Name Type Description r_shift_limit int, int) or int

Range for shifting the red channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-r_shift_limit, r_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

g_shift_limit int, int) or int

Range for shifting the green channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-g_shift_limit, g_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

b_shift_limit int, int) or int

Range for shifting the blue channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-b_shift_limit, b_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • Values are shifted independently for each channel
  • For uint8 images:
    • Input ranges like (-20, 20) represent pixel value shifts
    • A shift of 20 means adding 20 to that channel
    • Final values are clipped to [0, 255]
  • For float32 images:
    • Input ranges like (-0.1, 0.1) represent relative shifts
    • A shift of 0.1 means adding 0.1 to that channel
    • Final values are clipped to [0, 1]

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--shift-rgb-channels-of-uint8-image","title":"Shift RGB channels of uint8 image","text":"Python
>>> transform = A.RGBShift(\n...     r_shift_limit=30,  # Will sample red shift from [-30, 30]\n...     g_shift_limit=(-20, 20),  # Will sample green shift from [-20, 20]\n...     b_shift_limit=(-10, 10),  # Will sample blue shift from [-10, 10]\n...     p=1.0\n... )\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> shifted = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--same-effect-using-additivenoise","title":"Same effect using AdditiveNoise","text":"Python
>>> transform = A.AdditiveNoise(\n...     noise_type=\"uniform\",\n...     spatial_mode=\"constant\",  # One value per channel\n...     noise_params={\n...         \"ranges\": [(-30/255, 30/255), (-20/255, 20/255), (-10/255, 10/255)]\n...     },\n...     p=1.0\n... )\n

See Also: - AdditiveNoise: More general noise transform with various options: * Different noise distributions (uniform, gaussian, laplace, beta) * Spatial modes (constant, per-pixel, shared) * Approximation for faster computation - RandomToneCurve: For non-linear color transformations - RandomBrightnessContrast: For combined brightness and contrast adjustments - PlankianJitter: For color temperature adjustments - HueSaturationValue: For HSV color space adjustments - ColorJitter: For combined brightness, contrast, saturation adjustments

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RGBShift(AdditiveNoise):\n    \"\"\"Randomly shift values for each channel of the input RGB image.\n\n    A specialized version of AdditiveNoise that applies constant uniform shifts to RGB channels.\n    Each channel (R,G,B) can have its own shift range specified.\n\n    Args:\n        r_shift_limit ((int, int) or int): Range for shifting the red channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-r_shift_limit, r_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        g_shift_limit ((int, int) or int): Range for shifting the green channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-g_shift_limit, g_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        b_shift_limit ((int, int) or int): Range for shifting the blue channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-b_shift_limit, b_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Values are shifted independently for each channel\n        - For uint8 images:\n            * Input ranges like (-20, 20) represent pixel value shifts\n            * A shift of 20 means adding 20 to that channel\n            * Final values are clipped to [0, 255]\n        - For float32 images:\n            * Input ranges like (-0.1, 0.1) represent relative shifts\n            * A shift of 0.1 means adding 0.1 to that channel\n            * Final values are clipped to [0, 1]\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n\n        # Shift RGB channels of uint8 image\n        >>> transform = A.RGBShift(\n        ...     r_shift_limit=30,  # Will sample red shift from [-30, 30]\n        ...     g_shift_limit=(-20, 20),  # Will sample green shift from [-20, 20]\n        ...     b_shift_limit=(-10, 10),  # Will sample blue shift from [-10, 10]\n        ...     p=1.0\n        ... )\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> shifted = transform(image=image)[\"image\"]\n\n        # Same effect using AdditiveNoise\n        >>> transform = A.AdditiveNoise(\n        ...     noise_type=\"uniform\",\n        ...     spatial_mode=\"constant\",  # One value per channel\n        ...     noise_params={\n        ...         \"ranges\": [(-30/255, 30/255), (-20/255, 20/255), (-10/255, 10/255)]\n        ...     },\n        ...     p=1.0\n        ... )\n\n    See Also:\n        - AdditiveNoise: More general noise transform with various options:\n            * Different noise distributions (uniform, gaussian, laplace, beta)\n            * Spatial modes (constant, per-pixel, shared)\n            * Approximation for faster computation\n        - RandomToneCurve: For non-linear color transformations\n        - RandomBrightnessContrast: For combined brightness and contrast adjustments\n        - PlankianJitter: For color temperature adjustments\n        - HueSaturationValue: For HSV color space adjustments\n        - ColorJitter: For combined brightness, contrast, saturation adjustments\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        r_shift_limit: SymmetricRangeType\n        g_shift_limit: SymmetricRangeType\n        b_shift_limit: SymmetricRangeType\n\n    def __init__(\n        self,\n        r_shift_limit: ScaleFloatType = (-20, 20),\n        g_shift_limit: ScaleFloatType = (-20, 20),\n        b_shift_limit: ScaleFloatType = (-20, 20),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        # Convert RGB shift limits to normalized ranges if needed\n        def normalize_range(limit: tuple[float, float]) -> tuple[float, float]:\n            # If any value is > 1, assume uint8 range and normalize\n            if abs(limit[0]) > 1 or abs(limit[1]) > 1:\n                return (limit[0] / 255.0, limit[1] / 255.0)\n            return limit\n\n        ranges = [\n            normalize_range(cast(tuple[float, float], r_shift_limit)),\n            normalize_range(cast(tuple[float, float], g_shift_limit)),\n            normalize_range(cast(tuple[float, float], b_shift_limit)),\n        ]\n\n        # Initialize with fixed noise type and spatial mode\n        super().__init__(\n            noise_type=\"uniform\",\n            spatial_mode=\"constant\",\n            noise_params={\"ranges\": ranges},\n            approximation=1.0,\n            p=p,\n        )\n\n        # Store original limits for get_transform_init_args\n        self.r_shift_limit = cast(tuple[float, float], r_shift_limit)\n        self.g_shift_limit = cast(tuple[float, float], g_shift_limit)\n        self.b_shift_limit = cast(tuple[float, float], b_shift_limit)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"r_shift_limit\", \"g_shift_limit\", \"b_shift_limit\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomBrightnessContrast","title":"class RandomBrightnessContrast (brightness_limit=(-0.2, 0.2), contrast_limit=(-0.2, 0.2), brightness_by_max=True, ensure_safe_range=False, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly changes the brightness and contrast of the input image.

This transform adjusts the brightness and contrast of an image simultaneously, allowing for a wide range of lighting and contrast variations. It's particularly useful for data augmentation in computer vision tasks, helping models become more robust to different lighting conditions.

Parameters:

Name Type Description brightness_limit float | tuple[float, float]

Factor range for changing brightness. If a single float value is provided, the range will be (-brightness_limit, brightness_limit). Values should typically be in the range [-1.0, 1.0], where 0 means no change, 1.0 means maximum brightness, and -1.0 means minimum brightness. Default: (-0.2, 0.2).

contrast_limit float | tuple[float, float]

Factor range for changing contrast. If a single float value is provided, the range will be (-contrast_limit, contrast_limit). Values should typically be in the range [-1.0, 1.0], where 0 means no change, 1.0 means maximum increase in contrast, and -1.0 means maximum decrease in contrast. Default: (-0.2, 0.2).

brightness_by_max bool

If True, adjusts brightness by scaling pixel values up to the maximum value of the image's dtype. If False, uses the mean pixel value for adjustment. Default: True.

ensure_safe_range bool

If True, adjusts alpha and beta to prevent overflow/underflow. This ensures output values stay within the valid range for the image dtype without clipping. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The order of operation is: contrast adjustment, then brightness adjustment.
  • For uint8 images, the output is clipped to [0, 255] range.
  • For float32 images, the output is clipped to [0, 1] range.
  • The brightness_by_max parameter affects how brightness is adjusted:
  • If True, brightness adjustment is more pronounced and can lead to more saturated results.
  • If False, brightness adjustment is more subtle and preserves the overall lighting better.
  • This transform is useful for:
  • Simulating different lighting conditions
  • Enhancing low-light or overexposed images
  • Data augmentation to improve model robustness

Mathematical Formulation: Let a be the contrast adjustment factor and \u03b2 be the brightness adjustment factor. For each pixel value x: 1. Contrast adjustment: x' = clip((x - mean) * (1 + a) + mean) 2. Brightness adjustment: If brightness_by_max is True: x'' = clip(x' * (1 + \u03b2)) If brightness_by_max is False: x'' = clip(x' + \u03b2 * max_value) Where clip() ensures values stay within the valid range for the image dtype.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomBrightnessContrast(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-brightness-and-contrast-limits","title":"Custom brightness and contrast limits","text":"Python
>>> transform = A.RandomBrightnessContrast(\n...     brightness_limit=0.3,\n...     contrast_limit=0.3,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--adjust-brightness-based-on-mean-value","title":"Adjust brightness based on mean value","text":"Python
>>> transform = A.RandomBrightnessContrast(\n...     brightness_limit=0.2,\n...     contrast_limit=0.2,\n...     brightness_by_max=False,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Brightness: https://en.wikipedia.org/wiki/Brightness
  • Contrast: https://en.wikipedia.org/wiki/Contrast_(vision)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomBrightnessContrast(ImageOnlyTransform):\n    \"\"\"Randomly changes the brightness and contrast of the input image.\n\n    This transform adjusts the brightness and contrast of an image simultaneously, allowing for\n    a wide range of lighting and contrast variations. It's particularly useful for data augmentation\n    in computer vision tasks, helping models become more robust to different lighting conditions.\n\n    Args:\n        brightness_limit (float | tuple[float, float]): Factor range for changing brightness.\n            If a single float value is provided, the range will be (-brightness_limit, brightness_limit).\n            Values should typically be in the range [-1.0, 1.0], where 0 means no change,\n            1.0 means maximum brightness, and -1.0 means minimum brightness.\n            Default: (-0.2, 0.2).\n\n        contrast_limit (float | tuple[float, float]): Factor range for changing contrast.\n            If a single float value is provided, the range will be (-contrast_limit, contrast_limit).\n            Values should typically be in the range [-1.0, 1.0], where 0 means no change,\n            1.0 means maximum increase in contrast, and -1.0 means maximum decrease in contrast.\n            Default: (-0.2, 0.2).\n\n        brightness_by_max (bool): If True, adjusts brightness by scaling pixel values up to the\n            maximum value of the image's dtype. If False, uses the mean pixel value for adjustment.\n            Default: True.\n\n        ensure_safe_range (bool): If True, adjusts alpha and beta to prevent overflow/underflow.\n            This ensures output values stay within the valid range for the image dtype without clipping.\n            Default: False.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The order of operation is: contrast adjustment, then brightness adjustment.\n        - For uint8 images, the output is clipped to [0, 255] range.\n        - For float32 images, the output is clipped to [0, 1] range.\n        - The `brightness_by_max` parameter affects how brightness is adjusted:\n          * If True, brightness adjustment is more pronounced and can lead to more saturated results.\n          * If False, brightness adjustment is more subtle and preserves the overall lighting better.\n        - This transform is useful for:\n          * Simulating different lighting conditions\n          * Enhancing low-light or overexposed images\n          * Data augmentation to improve model robustness\n\n    Mathematical Formulation:\n        Let a be the contrast adjustment factor and \u03b2 be the brightness adjustment factor.\n        For each pixel value x:\n        1. Contrast adjustment: x' = clip((x - mean) * (1 + a) + mean)\n        2. Brightness adjustment:\n           If brightness_by_max is True:  x'' = clip(x' * (1 + \u03b2))\n           If brightness_by_max is False: x'' = clip(x' + \u03b2 * max_value)\n        Where clip() ensures values stay within the valid range for the image dtype.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomBrightnessContrast(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom brightness and contrast limits\n        >>> transform = A.RandomBrightnessContrast(\n        ...     brightness_limit=0.3,\n        ...     contrast_limit=0.3,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Adjust brightness based on mean value\n        >>> transform = A.RandomBrightnessContrast(\n        ...     brightness_limit=0.2,\n        ...     contrast_limit=0.2,\n        ...     brightness_by_max=False,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Brightness: https://en.wikipedia.org/wiki/Brightness\n        - Contrast: https://en.wikipedia.org/wiki/Contrast_(vision)\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness_limit: SymmetricRangeType\n        contrast_limit: SymmetricRangeType\n        brightness_by_max: bool\n        ensure_safe_range: bool\n\n    def __init__(\n        self,\n        brightness_limit: ScaleFloatType = (-0.2, 0.2),\n        contrast_limit: ScaleFloatType = (-0.2, 0.2),\n        brightness_by_max: bool = True,\n        ensure_safe_range: bool = False,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.brightness_limit = cast(tuple[float, float], brightness_limit)\n        self.contrast_limit = cast(tuple[float, float], contrast_limit)\n        self.brightness_by_max = brightness_by_max\n        self.ensure_safe_range = ensure_safe_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha: float,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return albucore.multiply_add(img, alpha, beta, inplace=False)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, float]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample initial values\n        alpha = 1.0 + self.py_random.uniform(*self.contrast_limit)\n        beta = self.py_random.uniform(*self.brightness_limit)\n\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n        # Scale beta according to brightness_by_max setting\n        beta = beta * max_value if self.brightness_by_max else beta * np.mean(image)\n\n        # Clip values to safe ranges if needed\n        if self.ensure_safe_range:\n            alpha, beta = fmain.get_safe_brightness_contrast_params(\n                alpha,\n                beta,\n                max_value,\n            )\n\n        return {\n            \"alpha\": alpha,\n            \"beta\": beta,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"brightness_limit\",\n            \"contrast_limit\",\n            \"brightness_by_max\",\n            \"ensure_safe_range\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomFog","title":"class RandomFog (fog_coef_lower=None, fog_coef_upper=None, alpha_coef=0.08, fog_coef_range=(0.3, 1), always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates fog for the image by adding random fog-like artifacts.

This transform creates a fog effect by generating semi-transparent overlays that mimic the visual characteristics of fog. The fog intensity and distribution can be controlled to create various fog-like conditions.

Parameters:

Name Type Description fog_coef_range tuple[float, float]

Range for fog intensity coefficient. Should be in [0, 1] range.

alpha_coef float

Transparency of the fog circles. Should be in [0, 1] range. Default: 0.08.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The fog effect is created by overlaying semi-transparent circles on the image.
  • Higher fog coefficient values result in denser fog effects.
  • The fog is typically denser in the center of the image and gradually decreases towards the edges.
  • This transform is useful for:
  • Simulating various weather conditions in outdoor scenes
  • Data augmentation for improving model robustness to foggy conditions
  • Creating atmospheric effects in image editing

Mathematical Formulation: For each fog particle: 1. A position (x, y) is randomly generated within the image. 2. A circle with random radius is drawn at this position. 3. The circle's alpha (transparency) is determined by the alpha_coef. 4. These circles are overlaid on the original image to create the fog effect.

The final pixel value is calculated as:\noutput = (1 - alpha) * original_pixel + alpha * fog_color\n\nwhere alpha is influenced by the fog_coef and alpha_coef parameters.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomFog(p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-fog-intensity-range","title":"Custom fog intensity range","text":"Python
>>> transform = A.RandomFog(fog_coef_lower=0.3, fog_coef_upper=0.8, p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--adjust-fog-transparency","title":"Adjust fog transparency","text":"Python
>>> transform = A.RandomFog(fog_coef_lower=0.2, fog_coef_upper=0.5, alpha_coef=0.1, p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n

References

  • Fog: https://en.wikipedia.org/wiki/Fog
  • Atmospheric perspective: https://en.wikipedia.org/wiki/Aerial_perspective

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomFog(ImageOnlyTransform):\n    \"\"\"Simulates fog for the image by adding random fog-like artifacts.\n\n    This transform creates a fog effect by generating semi-transparent overlays\n    that mimic the visual characteristics of fog. The fog intensity and distribution\n    can be controlled to create various fog-like conditions.\n\n    Args:\n        fog_coef_range (tuple[float, float]): Range for fog intensity coefficient. Should be in [0, 1] range.\n        alpha_coef (float): Transparency of the fog circles. Should be in [0, 1] range. Default: 0.08.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The fog effect is created by overlaying semi-transparent circles on the image.\n        - Higher fog coefficient values result in denser fog effects.\n        - The fog is typically denser in the center of the image and gradually decreases towards the edges.\n        - This transform is useful for:\n          * Simulating various weather conditions in outdoor scenes\n          * Data augmentation for improving model robustness to foggy conditions\n          * Creating atmospheric effects in image editing\n\n    Mathematical Formulation:\n        For each fog particle:\n        1. A position (x, y) is randomly generated within the image.\n        2. A circle with random radius is drawn at this position.\n        3. The circle's alpha (transparency) is determined by the alpha_coef.\n        4. These circles are overlaid on the original image to create the fog effect.\n\n        The final pixel value is calculated as:\n        output = (1 - alpha) * original_pixel + alpha * fog_color\n\n        where alpha is influenced by the fog_coef and alpha_coef parameters.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomFog(p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n        # Custom fog intensity range\n        >>> transform = A.RandomFog(fog_coef_lower=0.3, fog_coef_upper=0.8, p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n        # Adjust fog transparency\n        >>> transform = A.RandomFog(fog_coef_lower=0.2, fog_coef_upper=0.5, alpha_coef=0.1, p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Fog: https://en.wikipedia.org/wiki/Fog\n        - Atmospheric perspective: https://en.wikipedia.org/wiki/Aerial_perspective\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        fog_coef_lower: float | None = Field(\n            ge=0,\n            le=1,\n        )\n        fog_coef_upper: float | None = Field(\n            ge=0,\n            le=1,\n        )\n        fog_coef_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        alpha_coef: float = Field(ge=0, le=1)\n\n        @model_validator(mode=\"after\")\n        def validate_fog_coefficients(self) -> Self:\n            if self.fog_coef_lower is not None:\n                warn(\n                    \"`fog_coef_lower` is deprecated, use `fog_coef_range` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            if self.fog_coef_upper is not None:\n                warn(\n                    \"`fog_coef_upper` is deprecated, use `fog_coef_range` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            lower = self.fog_coef_lower if self.fog_coef_lower is not None else self.fog_coef_range[0]\n            upper = self.fog_coef_upper if self.fog_coef_upper is not None else self.fog_coef_range[1]\n            self.fog_coef_range = (lower, upper)\n\n            self.fog_coef_lower = None\n            self.fog_coef_upper = None\n\n            return self\n\n    def __init__(\n        self,\n        fog_coef_lower: float | None = None,\n        fog_coef_upper: float | None = None,\n        alpha_coef: float = 0.08,\n        fog_coef_range: tuple[float, float] = (0.3, 1),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.fog_coef_range = fog_coef_range\n        self.alpha_coef = alpha_coef\n\n    def apply(\n        self,\n        img: np.ndarray,\n        particle_positions: list[tuple[int, int]],\n        radiuses: list[int],\n        intensity: float,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.add_fog(\n            img,\n            intensity,\n            self.alpha_coef,\n            particle_positions,\n            radiuses,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        # Select a random fog intensity within the specified range\n        intensity = self.py_random.uniform(*self.fog_coef_range)\n\n        image_shape = params[\"shape\"][:2]\n\n        image_height, image_width = image_shape\n\n        # Calculate the size of the fog effect region based on image width and fog intensity\n        fog_region_size = max(1, int(image_width // 3 * intensity))\n\n        particle_positions = []\n\n        # Initialize the central region where fog will be most dense\n        center_x, center_y = (int(x) for x in fgeometric.center(image_shape))\n\n        # Define the initial size of the foggy area\n        current_width = image_width\n        current_height = image_height\n\n        # Define shrink factor for reducing the foggy area each iteration\n        shrink_factor = 0.1\n\n        max_iterations = 10  # Prevent infinite loop\n        iteration = 0\n\n        while current_width > fog_region_size and current_height > fog_region_size and iteration < max_iterations:\n            # Calculate the number of particles for this region\n            area = current_width * current_height\n            particles_in_region = int(\n                area / (fog_region_size * fog_region_size) * intensity * 10,\n            )\n\n            for _ in range(particles_in_region):\n                # Generate random positions within the current region\n                x = self.py_random.randint(\n                    center_x - current_width // 2,\n                    center_x + current_width // 2,\n                )\n                y = self.py_random.randint(\n                    center_y - current_height // 2,\n                    center_y + current_height // 2,\n                )\n                particle_positions.append((x, y))\n\n            # Shrink the region for the next iteration\n            current_width = int(current_width * (1 - shrink_factor))\n            current_height = int(current_height * (1 - shrink_factor))\n\n            iteration += 1\n\n        radiuses = fmain.get_fog_particle_radiuses(\n            image_shape,\n            len(particle_positions),\n            intensity,\n            self.random_generator,\n        )\n\n        return {\n            \"particle_positions\": particle_positions,\n            \"intensity\": intensity,\n            \"radiuses\": radiuses,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"fog_coef_range\", \"alpha_coef\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomGamma","title":"class RandomGamma (gamma_limit=(80, 120), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies random gamma correction to the input image.

Gamma correction, or simply gamma, is a nonlinear operation used to encode and decode luminance or tristimulus values in imaging systems. This transform can adjust the brightness of an image while preserving the relative differences between darker and lighter areas, making it useful for simulating different lighting conditions or correcting for display characteristics.

Parameters:

Name Type Description gamma_limit float | tuple[float, float]

If gamma_limit is a single float value, the range will be (1, gamma_limit). If it's a tuple of two floats, they will serve as the lower and upper bounds for gamma adjustment. Values are in terms of percentage change, e.g., (80, 120) means the gamma will be between 80% and 120% of the original. Default: (80, 120).

eps

A small value added to the gamma to avoid division by zero or log of zero errors. Default: 1e-7.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The gamma correction is applied using the formula: output = input^gamma
  • Gamma values > 1 will make the image darker, while values < 1 will make it brighter
  • This transform is particularly useful for:
  • Simulating different lighting conditions
  • Correcting for non-linear display characteristics
  • Enhancing contrast in certain regions of the image
  • Data augmentation in computer vision tasks

Mathematical Formulation: Let I be the input image and G (gamma) be the correction factor. The gamma correction is applied as follows: 1. Normalize the image to [0, 1] range: I_norm = I / 255 (for uint8 images) 2. Apply gamma correction: I_corrected = I_norm ^ (1 / G) 3. Scale back to original range: output = I_corrected * 255 (for uint8 images)

The actual gamma value used is calculated as:\nG = 1 + (random_value / 100), where random_value is sampled from gamma_limit range.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomGamma(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-gamma-range","title":"Custom gamma range","text":"Python
>>> transform = A.RandomGamma(gamma_limit=(50, 150), p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--applying-with-other-transforms","title":"Applying with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomGamma(gamma_limit=(80, 120), p=0.5),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction
  • Power law (Gamma) encoding: https://www.cambridgeincolour.com/tutorials/gamma-correction.htm

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomGamma(ImageOnlyTransform):\n    \"\"\"Applies random gamma correction to the input image.\n\n    Gamma correction, or simply gamma, is a nonlinear operation used to encode and decode luminance\n    or tristimulus values in imaging systems. This transform can adjust the brightness of an image\n    while preserving the relative differences between darker and lighter areas, making it useful\n    for simulating different lighting conditions or correcting for display characteristics.\n\n    Args:\n        gamma_limit (float | tuple[float, float]): If gamma_limit is a single float value, the range\n            will be (1, gamma_limit). If it's a tuple of two floats, they will serve as\n            the lower and upper bounds for gamma adjustment. Values are in terms of percentage change,\n            e.g., (80, 120) means the gamma will be between 80% and 120% of the original.\n            Default: (80, 120).\n        eps: A small value added to the gamma to avoid division by zero or log of zero errors.\n            Default: 1e-7.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The gamma correction is applied using the formula: output = input^gamma\n        - Gamma values > 1 will make the image darker, while values < 1 will make it brighter\n        - This transform is particularly useful for:\n          * Simulating different lighting conditions\n          * Correcting for non-linear display characteristics\n          * Enhancing contrast in certain regions of the image\n          * Data augmentation in computer vision tasks\n\n    Mathematical Formulation:\n        Let I be the input image and G (gamma) be the correction factor.\n        The gamma correction is applied as follows:\n        1. Normalize the image to [0, 1] range: I_norm = I / 255 (for uint8 images)\n        2. Apply gamma correction: I_corrected = I_norm ^ (1 / G)\n        3. Scale back to original range: output = I_corrected * 255 (for uint8 images)\n\n        The actual gamma value used is calculated as:\n        G = 1 + (random_value / 100), where random_value is sampled from gamma_limit range.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomGamma(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom gamma range\n        >>> transform = A.RandomGamma(gamma_limit=(50, 150), p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Applying with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomGamma(gamma_limit=(80, 120), p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction\n        - Power law (Gamma) encoding: https://www.cambridgeincolour.com/tutorials/gamma-correction.htm\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        gamma_limit: OnePlusFloatRangeType\n\n    def __init__(\n        self,\n        gamma_limit: ScaleFloatType = (80, 120),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.gamma_limit = cast(tuple[float, float], gamma_limit)\n\n    def apply(self, img: np.ndarray, gamma: float, **params: Any) -> np.ndarray:\n        return fmain.gamma_transform(img, gamma=gamma)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"gamma\": self.py_random.uniform(self.gamma_limit[0], self.gamma_limit[1]) / 100.0,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"gamma_limit\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomGravel","title":"class RandomGravel (gravel_roi=(0.1, 0.4, 0.9, 0.9), number_of_patches=2, always_apply=None, p=0.5) [view source on GitHub]","text":"

Adds gravel-like artifacts to the input image.

This transform simulates the appearance of gravel or small stones scattered across specific regions of an image. It's particularly useful for augmenting datasets of road or terrain images, adding realistic texture variations.

Parameters:

Name Type Description gravel_roi tuple[float, float, float, float]

Region of interest where gravel will be added, specified as (x_min, y_min, x_max, y_max) in relative coordinates [0, 1]. Default: (0.1, 0.4, 0.9, 0.9).

number_of_patches int

Number of gravel patch regions to generate within the ROI. Each patch will contain multiple gravel particles. Default: 2.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The gravel effect is created by modifying the saturation channel in the HLS color space.
  • Gravel particles are distributed within randomly generated patches inside the specified ROI.
  • This transform is particularly useful for:
  • Augmenting datasets for road condition analysis
  • Simulating variations in terrain for computer vision tasks
  • Adding realistic texture to synthetic images of outdoor scenes

Mathematical Formulation: For each gravel patch: 1. A rectangular region is randomly generated within the specified ROI. 2. Within this region, multiple gravel particles are placed. 3. For each particle: - Random (x, y) coordinates are generated within the patch. - A random radius (r) between 1 and 3 pixels is assigned. - A random saturation value (sat) between 0 and 255 is assigned. 4. The saturation channel of the image is modified for each particle: image_hls[y-r:y+r, x-r:x+r, 1] = sat

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomGravel(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-roi-and-number-of-patches","title":"Custom ROI and number of patches","text":"Python
>>> transform = A.RandomGravel(\n...     gravel_roi=(0.2, 0.2, 0.8, 0.8),\n...     number_of_patches=5,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--combining-with-other-transforms","title":"Combining with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomGravel(p=0.7),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Road surface textures: https://en.wikipedia.org/wiki/Road_surface
  • HLS color space: https://en.wikipedia.org/wiki/HSL_and_HSV

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomGravel(ImageOnlyTransform):\n    \"\"\"Adds gravel-like artifacts to the input image.\n\n    This transform simulates the appearance of gravel or small stones scattered across\n    specific regions of an image. It's particularly useful for augmenting datasets of\n    road or terrain images, adding realistic texture variations.\n\n    Args:\n        gravel_roi (tuple[float, float, float, float]): Region of interest where gravel\n            will be added, specified as (x_min, y_min, x_max, y_max) in relative coordinates\n            [0, 1]. Default: (0.1, 0.4, 0.9, 0.9).\n        number_of_patches (int): Number of gravel patch regions to generate within the ROI.\n            Each patch will contain multiple gravel particles. Default: 2.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The gravel effect is created by modifying the saturation channel in the HLS color space.\n        - Gravel particles are distributed within randomly generated patches inside the specified ROI.\n        - This transform is particularly useful for:\n          * Augmenting datasets for road condition analysis\n          * Simulating variations in terrain for computer vision tasks\n          * Adding realistic texture to synthetic images of outdoor scenes\n\n    Mathematical Formulation:\n        For each gravel patch:\n        1. A rectangular region is randomly generated within the specified ROI.\n        2. Within this region, multiple gravel particles are placed.\n        3. For each particle:\n           - Random (x, y) coordinates are generated within the patch.\n           - A random radius (r) between 1 and 3 pixels is assigned.\n           - A random saturation value (sat) between 0 and 255 is assigned.\n        4. The saturation channel of the image is modified for each particle:\n           image_hls[y-r:y+r, x-r:x+r, 1] = sat\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomGravel(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom ROI and number of patches\n        >>> transform = A.RandomGravel(\n        ...     gravel_roi=(0.2, 0.2, 0.8, 0.8),\n        ...     number_of_patches=5,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Combining with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomGravel(p=0.7),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Road surface textures: https://en.wikipedia.org/wiki/Road_surface\n        - HLS color space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        gravel_roi: tuple[float, float, float, float]\n        number_of_patches: int = Field(ge=1)\n\n        @model_validator(mode=\"after\")\n        def validate_gravel_roi(self) -> Self:\n            gravel_lower_x, gravel_lower_y, gravel_upper_x, gravel_upper_y = self.gravel_roi\n            if not 0 <= gravel_lower_x < gravel_upper_x <= 1 or not 0 <= gravel_lower_y < gravel_upper_y <= 1:\n                raise ValueError(f\"Invalid gravel_roi. Got: {self.gravel_roi}.\")\n            return self\n\n    def __init__(\n        self,\n        gravel_roi: tuple[float, float, float, float] = (0.1, 0.4, 0.9, 0.9),\n        number_of_patches: int = 2,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p, always_apply)\n        self.gravel_roi = gravel_roi\n        self.number_of_patches = number_of_patches\n\n    def generate_gravel_patch(\n        self,\n        rectangular_roi: tuple[int, int, int, int],\n    ) -> np.ndarray:\n        x_min, y_min, x_max, y_max = rectangular_roi\n        area = abs((x_max - x_min) * (y_max - y_min))\n        count = area // 10\n        gravels = np.empty([count, 2], dtype=np.int64)\n        gravels[:, 0] = self.random_generator.integers(x_min, x_max, count)\n        gravels[:, 1] = self.random_generator.integers(y_min, y_max, count)\n        return gravels\n\n    def apply(\n        self,\n        img: np.ndarray,\n        gravels_infos: list[Any],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_gravel(img, gravels_infos)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n\n        # Calculate ROI in pixels\n        x_min, y_min, x_max, y_max = (\n            int(coord * dim) for coord, dim in zip(self.gravel_roi, [width, height, width, height])\n        )\n\n        roi_width = x_max - x_min\n        roi_height = y_max - y_min\n\n        gravels_info = []\n\n        for _ in range(self.number_of_patches):\n            # Generate a random rectangular region within the ROI\n            patch_width = self.py_random.randint(roi_width // 10, roi_width // 5)\n            patch_height = self.py_random.randint(roi_height // 10, roi_height // 5)\n\n            patch_x = self.py_random.randint(x_min, x_max - patch_width)\n            patch_y = self.py_random.randint(y_min, y_max - patch_height)\n\n            # Generate gravel particles within this patch\n            num_particles = (patch_width * patch_height) // 100  # Adjust this divisor to control density\n\n            for _ in range(num_particles):\n                x = self.py_random.randint(patch_x, patch_x + patch_width)\n                y = self.py_random.randint(patch_y, patch_y + patch_height)\n                r = self.py_random.randint(1, 3)\n                sat = self.py_random.randint(0, 255)\n\n                gravels_info.append(\n                    [\n                        max(y - r, 0),  # min_y\n                        min(y + r, height - 1),  # max_y\n                        max(x - r, 0),  # min_x\n                        min(x + r, width - 1),  # max_x\n                        sat,  # saturation\n                    ],\n                )\n\n        return {\"gravels_infos\": np.array(gravels_info, dtype=np.int64)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"gravel_roi\", \"number_of_patches\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomRain","title":"class RandomRain (slant_lower=None, slant_upper=None, slant_range=(-10, 10), drop_length=20, drop_width=1, drop_color=(200, 200, 200), blur_value=7, brightness_coefficient=0.7, rain_type='default', always_apply=None, p=0.5) [view source on GitHub]","text":"

Adds rain effects to an image.

This transform simulates rainfall by overlaying semi-transparent streaks onto the image, creating a realistic rain effect. It can be used to augment datasets for computer vision tasks that need to perform well in rainy conditions.

Parameters:

Name Type Description slant_range tuple[int, int]

Range for the rain slant angle in degrees. Negative values slant to the left, positive to the right. Default: (-10, 10).

drop_length int

Length of the rain drops in pixels. Default: 20.

drop_width int

Width of the rain drops in pixels. Default: 1.

drop_color tuple[int, int, int]

Color of the rain drops in RGB format. Default: (200, 200, 200).

blur_value int

Blur value for simulating rain effect. Rainy views are typically blurry. Default: 7.

brightness_coefficient float

Coefficient to adjust the brightness of the image. Rainy scenes are usually darker. Should be in the range (0, 1]. Default: 0.7.

rain_type Literal[\"drizzle\", \"heavy\", \"torrential\", \"default\"]

Type of rain to simulate.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The rain effect is created by drawing semi-transparent lines on the image.
  • The slant of the rain can be controlled to simulate wind effects.
  • Different rain types (drizzle, heavy, torrential) adjust the density and appearance of the rain.
  • The transform also adjusts image brightness and applies a blur to simulate the visual effects of rain.
  • This transform is particularly useful for:
  • Augmenting datasets for autonomous driving in rainy conditions
  • Testing the robustness of computer vision models to weather effects
  • Creating realistic rainy scenes for image editing or film production

Mathematical Formulation: For each raindrop: 1. Start position (x1, y1) is randomly generated within the image. 2. End position (x2, y2) is calculated based on drop_length and slant: x2 = x1 + drop_length * sin(slant) y2 = y1 + drop_length * cos(slant) 3. A line is drawn from (x1, y1) to (x2, y2) with the specified drop_color and drop_width. 4. The image is then blurred and its brightness is adjusted.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomRain(p=1.0)\n>>> rainy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-rain-parameters","title":"Custom rain parameters","text":"Python
>>> transform = A.RandomRain(\n...     slant_range=(-15, 15),\n...     drop_length=30,\n...     drop_width=2,\n...     drop_color=(180, 180, 180),\n...     blur_value=5,\n...     brightness_coefficient=0.8,\n...     p=1.0\n... )\n>>> rainy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--simulating-heavy-rain","title":"Simulating heavy rain","text":"Python
>>> transform = A.RandomRain(rain_type=\"heavy\", p=1.0)\n>>> heavy_rain_image = transform(image=image)[\"image\"]\n

References

  • Rain visualization techniques: https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-27-real-time-rain-rendering
  • Weather effects in computer vision: https://www.sciencedirect.com/science/article/pii/S1077314220300692

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomRain(ImageOnlyTransform):\n    \"\"\"Adds rain effects to an image.\n\n    This transform simulates rainfall by overlaying semi-transparent streaks onto the image,\n    creating a realistic rain effect. It can be used to augment datasets for computer vision\n    tasks that need to perform well in rainy conditions.\n\n    Args:\n        slant_range (tuple[int, int]): Range for the rain slant angle in degrees.\n            Negative values slant to the left, positive to the right. Default: (-10, 10).\n        drop_length (int): Length of the rain drops in pixels. Default: 20.\n        drop_width (int): Width of the rain drops in pixels. Default: 1.\n        drop_color (tuple[int, int, int]): Color of the rain drops in RGB format. Default: (200, 200, 200).\n        blur_value (int): Blur value for simulating rain effect. Rainy views are typically blurry. Default: 7.\n        brightness_coefficient (float): Coefficient to adjust the brightness of the image.\n            Rainy scenes are usually darker. Should be in the range (0, 1]. Default: 0.7.\n        rain_type (Literal[\"drizzle\", \"heavy\", \"torrential\", \"default\"]): Type of rain to simulate.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The rain effect is created by drawing semi-transparent lines on the image.\n        - The slant of the rain can be controlled to simulate wind effects.\n        - Different rain types (drizzle, heavy, torrential) adjust the density and appearance of the rain.\n        - The transform also adjusts image brightness and applies a blur to simulate the visual effects of rain.\n        - This transform is particularly useful for:\n          * Augmenting datasets for autonomous driving in rainy conditions\n          * Testing the robustness of computer vision models to weather effects\n          * Creating realistic rainy scenes for image editing or film production\n\n    Mathematical Formulation:\n        For each raindrop:\n        1. Start position (x1, y1) is randomly generated within the image.\n        2. End position (x2, y2) is calculated based on drop_length and slant:\n           x2 = x1 + drop_length * sin(slant)\n           y2 = y1 + drop_length * cos(slant)\n        3. A line is drawn from (x1, y1) to (x2, y2) with the specified drop_color and drop_width.\n        4. The image is then blurred and its brightness is adjusted.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomRain(p=1.0)\n        >>> rainy_image = transform(image=image)[\"image\"]\n\n        # Custom rain parameters\n        >>> transform = A.RandomRain(\n        ...     slant_range=(-15, 15),\n        ...     drop_length=30,\n        ...     drop_width=2,\n        ...     drop_color=(180, 180, 180),\n        ...     blur_value=5,\n        ...     brightness_coefficient=0.8,\n        ...     p=1.0\n        ... )\n        >>> rainy_image = transform(image=image)[\"image\"]\n\n        # Simulating heavy rain\n        >>> transform = A.RandomRain(rain_type=\"heavy\", p=1.0)\n        >>> heavy_rain_image = transform(image=image)[\"image\"]\n\n    References:\n        - Rain visualization techniques: https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-27-real-time-rain-rendering\n        - Weather effects in computer vision: https://www.sciencedirect.com/science/article/pii/S1077314220300692\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        slant_lower: int | None = Field(default=None)\n        slant_upper: int | None = Field(default=None)\n        slant_range: Annotated[tuple[float, float], AfterValidator(nondecreasing)]\n        drop_length: int = Field(ge=1)\n        drop_width: int = Field(ge=1)\n        drop_color: tuple[int, int, int]\n        blur_value: int = Field(ge=1)\n        brightness_coefficient: float = Field(gt=0, le=1)\n        rain_type: RainMode\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            if self.slant_lower is not None or self.slant_upper is not None:\n                if self.slant_lower is not None:\n                    warn(\n                        \"`slant_lower` deprecated. Use `slant_range` as tuple (slant_lower, slant_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.slant_upper is not None:\n                    warn(\n                        \"`slant_upper` deprecated. Use `slant_range` as tuple (slant_lower, slant_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.slant_lower if self.slant_lower is not None else self.slant_range[0]\n                upper = self.slant_upper if self.slant_upper is not None else self.slant_range[1]\n                self.slant_range = (lower, upper)\n                self.slant_lower = None\n                self.slant_upper = None\n\n            # Validate the slant_range\n            if not (-MAX_RAIN_ANGLE <= self.slant_range[0] <= self.slant_range[1] <= MAX_RAIN_ANGLE):\n                raise ValueError(\n                    f\"slant_range values should be increasing within [-{MAX_RAIN_ANGLE}, {MAX_RAIN_ANGLE}] range.\",\n                )\n            return self\n\n    def __init__(\n        self,\n        slant_lower: int | None = None,\n        slant_upper: int | None = None,\n        slant_range: tuple[int, int] = (-10, 10),\n        drop_length: int = 20,\n        drop_width: int = 1,\n        drop_color: tuple[int, int, int] = (200, 200, 200),\n        blur_value: int = 7,\n        brightness_coefficient: float = 0.7,\n        rain_type: RainMode = \"default\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.slant_range = slant_range\n        self.drop_length = drop_length\n        self.drop_width = drop_width\n        self.drop_color = drop_color\n        self.blur_value = blur_value\n        self.brightness_coefficient = brightness_coefficient\n        self.rain_type = rain_type\n\n    def apply(\n        self,\n        img: np.ndarray,\n        slant: int,\n        drop_length: int,\n        rain_drops: list[tuple[int, int]],\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n\n        return fmain.add_rain(\n            img,\n            slant,\n            drop_length,\n            self.drop_width,\n            self.drop_color,\n            self.blur_value,\n            self.brightness_coefficient,\n            rain_drops,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        slant = int(self.py_random.uniform(*self.slant_range))\n\n        height, width = params[\"shape\"][:2]\n        area = height * width\n\n        if self.rain_type == \"drizzle\":\n            num_drops = area // 770\n            drop_length = 10\n        elif self.rain_type == \"heavy\":\n            num_drops = width * height // 600\n            drop_length = 30\n        elif self.rain_type == \"torrential\":\n            num_drops = area // 500\n            drop_length = 60\n        else:\n            drop_length = self.drop_length\n            num_drops = area // 600\n\n        rain_drops = []\n\n        for _ in range(num_drops):  # If You want heavy rain, try increasing this\n            x = self.py_random.randint(slant, width) if slant < 0 else self.py_random.randint(0, max(width - slant, 0))\n            y = self.py_random.randint(0, max(height - drop_length, 0))\n\n            rain_drops.append((x, y))\n\n        return {\"drop_length\": drop_length, \"slant\": slant, \"rain_drops\": rain_drops}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"slant_range\",\n            \"drop_length\",\n            \"drop_width\",\n            \"drop_color\",\n            \"blur_value\",\n            \"brightness_coefficient\",\n            \"rain_type\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomShadow","title":"class RandomShadow (shadow_roi=(0, 0.5, 1, 1), num_shadows_limit=(1, 2), num_shadows_lower=None, num_shadows_upper=None, shadow_dimension=5, shadow_intensity_range=(0.5, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates shadows for the image by reducing the brightness of the image in shadow regions.

This transform adds realistic shadow effects to images, which can be useful for augmenting datasets for outdoor scene analysis, autonomous driving, or any computer vision task where shadows may be present.

Parameters:

Name Type Description shadow_roi tuple[float, float, float, float]

Region of the image where shadows will appear (x_min, y_min, x_max, y_max). All values should be in range [0, 1]. Default: (0, 0.5, 1, 1).

num_shadows_limit tuple[int, int]

Lower and upper limits for the possible number of shadows. Default: (1, 2).

shadow_dimension int

Number of edges in the shadow polygons. Default: 5.

shadow_intensity_range tuple[float, float]

Range for the shadow intensity. Larger value means darker shadow. Should be two float values between 0 and 1. Default: (0.5, 0.5).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • Shadows are created by generating random polygons within the specified ROI and reducing the brightness of the image in these areas.
  • The number of shadows, their shapes, and intensities can be randomized for variety.
  • This transform is particularly useful for:
  • Augmenting datasets for outdoor scene understanding
  • Improving robustness of object detection models to shadowed conditions
  • Simulating different lighting conditions in synthetic datasets

Mathematical Formulation: For each shadow: 1. A polygon with shadow_dimension vertices is generated within the shadow ROI. 2. The shadow intensity a is randomly chosen from shadow_intensity_range. 3. For each pixel (x, y) within the polygon: new_pixel_value = original_pixel_value * (1 - a)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomShadow(p=1.0)\n>>> shadowed_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-shadow-parameters","title":"Custom shadow parameters","text":"Python
>>> transform = A.RandomShadow(\n...     shadow_roi=(0.2, 0.2, 0.8, 0.8),\n...     num_shadows_limit=(2, 4),\n...     shadow_dimension=8,\n...     shadow_intensity_range=(0.3, 0.7),\n...     p=1.0\n... )\n>>> shadowed_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--combining-with-other-transforms","title":"Combining with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomShadow(p=0.5),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Shadow detection and removal: https://www.sciencedirect.com/science/article/pii/S1047320315002035
  • Shadows in computer vision: https://en.wikipedia.org/wiki/Shadow_detection

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomShadow(ImageOnlyTransform):\n    \"\"\"Simulates shadows for the image by reducing the brightness of the image in shadow regions.\n\n    This transform adds realistic shadow effects to images, which can be useful for augmenting\n    datasets for outdoor scene analysis, autonomous driving, or any computer vision task where\n    shadows may be present.\n\n    Args:\n        shadow_roi (tuple[float, float, float, float]): Region of the image where shadows\n            will appear (x_min, y_min, x_max, y_max). All values should be in range [0, 1].\n            Default: (0, 0.5, 1, 1).\n        num_shadows_limit (tuple[int, int]): Lower and upper limits for the possible number of shadows.\n            Default: (1, 2).\n        shadow_dimension (int): Number of edges in the shadow polygons. Default: 5.\n        shadow_intensity_range (tuple[float, float]): Range for the shadow intensity. Larger value\n            means darker shadow. Should be two float values between 0 and 1. Default: (0.5, 0.5).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - Shadows are created by generating random polygons within the specified ROI and\n          reducing the brightness of the image in these areas.\n        - The number of shadows, their shapes, and intensities can be randomized for variety.\n        - This transform is particularly useful for:\n          * Augmenting datasets for outdoor scene understanding\n          * Improving robustness of object detection models to shadowed conditions\n          * Simulating different lighting conditions in synthetic datasets\n\n    Mathematical Formulation:\n        For each shadow:\n        1. A polygon with `shadow_dimension` vertices is generated within the shadow ROI.\n        2. The shadow intensity a is randomly chosen from `shadow_intensity_range`.\n        3. For each pixel (x, y) within the polygon:\n           new_pixel_value = original_pixel_value * (1 - a)\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomShadow(p=1.0)\n        >>> shadowed_image = transform(image=image)[\"image\"]\n\n        # Custom shadow parameters\n        >>> transform = A.RandomShadow(\n        ...     shadow_roi=(0.2, 0.2, 0.8, 0.8),\n        ...     num_shadows_limit=(2, 4),\n        ...     shadow_dimension=8,\n        ...     shadow_intensity_range=(0.3, 0.7),\n        ...     p=1.0\n        ... )\n        >>> shadowed_image = transform(image=image)[\"image\"]\n\n        # Combining with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomShadow(p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Shadow detection and removal: https://www.sciencedirect.com/science/article/pii/S1047320315002035\n        - Shadows in computer vision: https://en.wikipedia.org/wiki/Shadow_detection\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        shadow_roi: tuple[float, float, float, float]\n        num_shadows_limit: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n        num_shadows_lower: int | None\n        num_shadows_upper: int | None\n        shadow_dimension: int = Field(ge=3)\n\n        shadow_intensity_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_shadows(self) -> Self:\n            if self.num_shadows_lower is not None:\n                warn(\n                    \"`num_shadows_lower` is deprecated. Use `num_shadows_limit` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.num_shadows_upper is not None:\n                warn(\n                    \"`num_shadows_upper` is deprecated. Use `num_shadows_limit` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.num_shadows_lower is not None or self.num_shadows_upper is not None:\n                num_shadows_lower = (\n                    self.num_shadows_lower if self.num_shadows_lower is not None else self.num_shadows_limit[0]\n                )\n                num_shadows_upper = (\n                    self.num_shadows_upper if self.num_shadows_upper is not None else self.num_shadows_limit[1]\n                )\n\n                self.num_shadows_limit = (num_shadows_lower, num_shadows_upper)\n                self.num_shadows_lower = None\n                self.num_shadows_upper = None\n\n            shadow_lower_x, shadow_lower_y, shadow_upper_x, shadow_upper_y = self.shadow_roi\n\n            if not 0 <= shadow_lower_x <= shadow_upper_x <= 1 or not 0 <= shadow_lower_y <= shadow_upper_y <= 1:\n                raise ValueError(f\"Invalid shadow_roi. Got: {self.shadow_roi}\")\n\n            if isinstance(self.shadow_intensity_range, float):\n                if not (0 <= self.shadow_intensity_range <= 1):\n                    raise ValueError(\n                        f\"shadow_intensity_range value should be within [0, 1] range. \"\n                        f\"Got: {self.shadow_intensity_range}\",\n                    )\n            elif isinstance(self.shadow_intensity_range, tuple):\n                if not (0 <= self.shadow_intensity_range[0] <= self.shadow_intensity_range[1] <= 1):\n                    raise ValueError(\n                        f\"shadow_intensity_range values should be within [0, 1] range and increasing. \"\n                        f\"Got: {self.shadow_intensity_range}\",\n                    )\n            else:\n                raise TypeError(\n                    \"shadow_intensity_range should be an float or a tuple of floats.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        shadow_roi: tuple[float, float, float, float] = (0, 0.5, 1, 1),\n        num_shadows_limit: tuple[int, int] = (1, 2),\n        num_shadows_lower: int | None = None,\n        num_shadows_upper: int | None = None,\n        shadow_dimension: int = 5,\n        shadow_intensity_range: tuple[float, float] = (0.5, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.shadow_roi = shadow_roi\n        self.shadow_dimension = shadow_dimension\n        self.num_shadows_limit = num_shadows_limit\n        self.shadow_intensity_range = shadow_intensity_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        vertices_list: list[np.ndarray],\n        intensities: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_shadow(img, vertices_list, intensities)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, list[np.ndarray]]:\n        height, width = params[\"shape\"][:2]\n\n        num_shadows = self.py_random.randint(*self.num_shadows_limit)\n\n        x_min, y_min, x_max, y_max = self.shadow_roi\n\n        x_min = int(x_min * width)\n        x_max = int(x_max * width)\n        y_min = int(y_min * height)\n        y_max = int(y_max * height)\n\n        vertices_list = [\n            np.stack(\n                [\n                    self.random_generator.integers(\n                        x_min,\n                        x_max,\n                        size=self.shadow_dimension,\n                    ),\n                    self.random_generator.integers(\n                        y_min,\n                        y_max,\n                        size=self.shadow_dimension,\n                    ),\n                ],\n                axis=1,\n            )\n            for _ in range(num_shadows)\n        ]\n\n        # Sample shadow intensity for each shadow\n        intensities = self.random_generator.uniform(\n            *self.shadow_intensity_range,\n            size=num_shadows,\n        )\n\n        return {\"vertices_list\": vertices_list, \"intensities\": intensities}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"shadow_roi\",\n            \"num_shadows_limit\",\n            \"shadow_dimension\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomSnow","title":"class RandomSnow (snow_point_lower=None, snow_point_upper=None, brightness_coeff=2.5, snow_point_range=(0.1, 0.3), method='bleach', always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies a random snow effect to the input image.

This transform simulates snowfall by either bleaching out some pixel values or adding a snow texture to the image, depending on the chosen method.

Parameters:

Name Type Description snow_point_range tuple[float, float]

Range for the snow point threshold. Both values should be in the (0, 1) range. Default: (0.1, 0.3).

brightness_coeff float

Coefficient applied to increase the brightness of pixels below the snow_point threshold. Larger values lead to more pronounced snow effects. Should be > 0. Default: 2.5.

method Literal[\"bleach\", \"texture\"]

The snow simulation method to use. Options are: - \"bleach\": Uses a simple pixel value thresholding technique. - \"texture\": Applies a more realistic snow texture overlay. Default: \"texture\".

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • The \"bleach\" method increases the brightness of pixels above a certain threshold, creating a simple snow effect. This method is faster but may look less realistic.
  • The \"texture\" method creates a more realistic snow effect through the following steps:
  • Converts the image to HSV color space for better control over brightness.
  • Increases overall image brightness to simulate the reflective nature of snow.
  • Generates a snow texture using Gaussian noise, which is then smoothed with a Gaussian filter.
  • Applies a depth effect to the snow texture, making it more prominent at the top of the image.
  • Blends the snow texture with the original image using alpha compositing.
  • Adds a slight blue tint to simulate the cool color of snow.
  • Adds random sparkle effects to simulate light reflecting off snow crystals. This method produces a more realistic result but is computationally more expensive.

Mathematical Formulation: For the \"bleach\" method: Let L be the lightness channel in HLS color space. For each pixel (i, j): If L[i, j] > snow_point: L[i, j] = L[i, j] * brightness_coeff

For the \"texture\" method:\n1. Brightness adjustment: V_new = V * (1 + brightness_coeff * snow_point)\n2. Snow texture generation: T = GaussianFilter(GaussianNoise(\u03bc=0.5, sigma=0.3))\n3. Depth effect: D = LinearGradient(1.0 to 0.2)\n4. Final pixel value: P = (1 - alpha) * original_pixel + alpha * (T * D * 255)\n   where alpha is the snow intensity factor derived from snow_point.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage-bleach-method","title":"Default usage (bleach method)","text":"Python
>>> transform = A.RandomSnow(p=1.0)\n>>> snowy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--using-texture-method-with-custom-parameters","title":"Using texture method with custom parameters","text":"Python
>>> transform = A.RandomSnow(\n...     snow_point_range=(0.2, 0.4),\n...     brightness_coeff=2.0,\n...     method=\"texture\",\n...     p=1.0\n... )\n>>> snowy_image = transform(image=image)[\"image\"]\n

References

  • Bleach method: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library
  • Texture method: Inspired by computer graphics techniques for snow rendering and atmospheric scattering simulations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomSnow(ImageOnlyTransform):\n    \"\"\"Applies a random snow effect to the input image.\n\n    This transform simulates snowfall by either bleaching out some pixel values or\n    adding a snow texture to the image, depending on the chosen method.\n\n    Args:\n        snow_point_range (tuple[float, float]): Range for the snow point threshold.\n            Both values should be in the (0, 1) range. Default: (0.1, 0.3).\n        brightness_coeff (float): Coefficient applied to increase the brightness of pixels\n            below the snow_point threshold. Larger values lead to more pronounced snow effects.\n            Should be > 0. Default: 2.5.\n        method (Literal[\"bleach\", \"texture\"]): The snow simulation method to use. Options are:\n            - \"bleach\": Uses a simple pixel value thresholding technique.\n            - \"texture\": Applies a more realistic snow texture overlay.\n            Default: \"texture\".\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The \"bleach\" method increases the brightness of pixels above a certain threshold,\n          creating a simple snow effect. This method is faster but may look less realistic.\n        - The \"texture\" method creates a more realistic snow effect through the following steps:\n          1. Converts the image to HSV color space for better control over brightness.\n          2. Increases overall image brightness to simulate the reflective nature of snow.\n          3. Generates a snow texture using Gaussian noise, which is then smoothed with a Gaussian filter.\n          4. Applies a depth effect to the snow texture, making it more prominent at the top of the image.\n          5. Blends the snow texture with the original image using alpha compositing.\n          6. Adds a slight blue tint to simulate the cool color of snow.\n          7. Adds random sparkle effects to simulate light reflecting off snow crystals.\n          This method produces a more realistic result but is computationally more expensive.\n\n    Mathematical Formulation:\n        For the \"bleach\" method:\n        Let L be the lightness channel in HLS color space.\n        For each pixel (i, j):\n        If L[i, j] > snow_point:\n            L[i, j] = L[i, j] * brightness_coeff\n\n        For the \"texture\" method:\n        1. Brightness adjustment: V_new = V * (1 + brightness_coeff * snow_point)\n        2. Snow texture generation: T = GaussianFilter(GaussianNoise(\u03bc=0.5, sigma=0.3))\n        3. Depth effect: D = LinearGradient(1.0 to 0.2)\n        4. Final pixel value: P = (1 - alpha) * original_pixel + alpha * (T * D * 255)\n           where alpha is the snow intensity factor derived from snow_point.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage (bleach method)\n        >>> transform = A.RandomSnow(p=1.0)\n        >>> snowy_image = transform(image=image)[\"image\"]\n\n        # Using texture method with custom parameters\n        >>> transform = A.RandomSnow(\n        ...     snow_point_range=(0.2, 0.4),\n        ...     brightness_coeff=2.0,\n        ...     method=\"texture\",\n        ...     p=1.0\n        ... )\n        >>> snowy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Bleach method: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n        - Texture method: Inspired by computer graphics techniques for snow rendering\n          and atmospheric scattering simulations.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        snow_point_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        snow_point_lower: float | None = Field(\n            gt=0,\n            lt=1,\n        )\n        snow_point_upper: float | None = Field(\n            gt=0,\n            lt=1,\n        )\n        brightness_coeff: float = Field(gt=0)\n        method: Literal[\"bleach\", \"texture\"]\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            if self.snow_point_lower is not None or self.snow_point_upper is not None:\n                if self.snow_point_lower is not None:\n                    warn(\n                        \"`snow_point_lower` deprecated. Use `snow_point_range` as tuple\"\n                        \" (snow_point_lower, snow_point_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.snow_point_upper is not None:\n                    warn(\n                        \"`snow_point_upper` deprecated. Use `snow_point_range` as tuple\"\n                        \"(snow_point_lower, snow_point_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.snow_point_lower if self.snow_point_lower is not None else self.snow_point_range[0]\n                upper = self.snow_point_upper if self.snow_point_upper is not None else self.snow_point_range[1]\n                self.snow_point_range = (lower, upper)\n                self.snow_point_lower = None\n                self.snow_point_upper = None\n\n            # Validate the snow_point_range\n            if not (0 < self.snow_point_range[0] <= self.snow_point_range[1] < 1):\n                raise ValueError(\n                    \"snow_point_range values should be increasing within (0, 1) range.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        snow_point_lower: float | None = None,\n        snow_point_upper: float | None = None,\n        brightness_coeff: float = 2.5,\n        snow_point_range: tuple[float, float] = (0.1, 0.3),\n        method: Literal[\"bleach\", \"texture\"] = \"bleach\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.snow_point_range = snow_point_range\n        self.brightness_coeff = brightness_coeff\n        self.method = method\n\n    def apply(\n        self,\n        img: np.ndarray,\n        snow_point: float,\n        snow_texture: np.ndarray,\n        sparkle_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n\n        if self.method == \"bleach\":\n            return fmain.add_snow_bleach(img, snow_point, self.brightness_coeff)\n        if self.method == \"texture\":\n            return fmain.add_snow_texture(\n                img,\n                snow_point,\n                self.brightness_coeff,\n                snow_texture,\n                sparkle_mask,\n            )\n\n        raise ValueError(f\"Unknown snow method: {self.method}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray | None]:\n        image_shape = params[\"shape\"][:2]\n        result = {\n            \"snow_point\": self.py_random.uniform(*self.snow_point_range),\n            \"snow_texture\": None,\n            \"sparkle_mask\": None,\n        }\n\n        if self.method == \"texture\":\n            snow_texture, sparkle_mask = fmain.generate_snow_textures(\n                img_shape=image_shape,\n                random_generator=self.random_generator,\n            )\n            result[\"snow_texture\"] = snow_texture\n            result[\"sparkle_mask\"] = sparkle_mask\n\n        return result\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"snow_point_range\", \"brightness_coeff\", \"method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomSunFlare","title":"class RandomSunFlare (flare_roi=(0, 0, 1, 0.5), angle_lower=None, angle_upper=None, num_flare_circles_lower=None, num_flare_circles_upper=None, src_radius=400, src_color=(255, 255, 255), angle_range=(0, 1), num_flare_circles_range=(6, 10), method='overlay', always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates a sun flare effect on the image by adding circles of light.

This transform creates a sun flare effect by overlaying multiple semi-transparent circles of varying sizes and intensities along a line originating from a \"sun\" point. It offers two methods: a simple overlay technique and a more complex physics-based approach.

Parameters:

Name Type Description flare_roi tuple[float, float, float, float]

Region of interest where the sun flare can appear. Values are in the range [0, 1] and represent (x_min, y_min, x_max, y_max) in relative coordinates. Default: (0, 0, 1, 0.5).

angle_range tuple[float, float]

Range of angles (in radians) for the flare direction. Values should be in the range [0, 1], where 0 represents 0 radians and 1 represents 2\u03c0 radians. Default: (0, 1).

num_flare_circles_range tuple[int, int]

Range for the number of flare circles to generate. Default: (6, 10).

src_radius int

Radius of the sun circle in pixels. Default: 400.

src_color tuple[int, int, int]

Color of the sun in RGB format. Default: (255, 255, 255).

method Literal[\"overlay\", \"physics_based\"]

Method to use for generating the sun flare. \"overlay\" uses a simple alpha blending technique, while \"physics_based\" simulates more realistic optical phenomena. Default: \"physics_based\".

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

The transform offers two methods for generating sun flares:

  1. Overlay Method (\"overlay\"):
  2. Creates a simple sun flare effect using basic alpha blending.
  3. Steps: a. Generate the main sun circle with a radial gradient. b. Create smaller flare circles along the flare line. c. Blend these elements with the original image using alpha compositing.
  4. Characteristics:

    • Faster computation
    • Less realistic appearance
    • Suitable for basic augmentation or when performance is a priority
  5. Physics-based Method (\"physics_based\"):

  6. Simulates more realistic optical phenomena observed in actual lens flares.
  7. Steps: a. Create a separate flare layer for complex manipulations. b. Add the main sun circle and diffraction spikes to simulate light diffraction. c. Generate and add multiple flare circles with varying properties. d. Apply Gaussian blur to create a soft, glowing effect. e. Create and apply a radial gradient mask for natural fading from the center. f. Simulate chromatic aberration by applying different blurs to color channels. g. Blend the flare with the original image using screen blending mode.
  8. Characteristics:
    • More computationally intensive
    • Produces more realistic and visually appealing results
    • Includes effects like diffraction spikes and chromatic aberration
    • Suitable for high-quality augmentation or realistic image synthesis

Mathematical Formulation: For both methods: 1. Sun position (x_s, y_s) is randomly chosen within the specified ROI. 2. Flare angle \u03b8 is randomly chosen from the angle_range. 3. For each flare circle i: - Position (x_i, y_i) = (x_s + t_i * cos(\u03b8), y_s + t_i * sin(\u03b8)) where t_i is a random distance along the flare line. - Radius r_i is randomly chosen, with larger circles closer to the sun. - Alpha (transparency) alpha_i is randomly chosen in the range [0.05, 0.2]. - Color (R_i, G_i, B_i) is randomly chosen close to src_color.

Overlay method blending:\nnew_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\nPhysics-based method blending:\nnew_pixel = 255 - ((255 - original_pixel) * (255 - flare_pixel) / 255)\n\n4. Each flare circle is blended with the image using alpha compositing:\n   new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-sun-flare-overlay-method","title":"Default sun flare (overlay method)","text":"Python
>>> transform = A.RandomSunFlare(p=1.0)\n>>> flared_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--physics-based-sun-flare-with-custom-parameters","title":"Physics-based sun flare with custom parameters","text":""},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-sun-flare","title":"Default sun flare","text":"Python
>>> transform = A.RandomSunFlare(p=1.0)\n>>> flared_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-sun-flare-parameters","title":"Custom sun flare parameters","text":"Python
>>> transform = A.RandomSunFlare(\n...     flare_roi=(0.1, 0, 0.9, 0.3),\n...     angle_range=(0.25, 0.75),\n...     num_flare_circles_range=(5, 15),\n...     src_radius=200,\n...     src_color=(255, 200, 100),\n...     method=\"physics_based\",\n...     p=1.0\n... )\n>>> flared_image = transform(image=image)[\"image\"]\n

References

  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Diffraction: https://en.wikipedia.org/wiki/Diffraction
  • Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration
  • Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomSunFlare(ImageOnlyTransform):\n    \"\"\"Simulates a sun flare effect on the image by adding circles of light.\n\n    This transform creates a sun flare effect by overlaying multiple semi-transparent\n    circles of varying sizes and intensities along a line originating from a \"sun\" point.\n    It offers two methods: a simple overlay technique and a more complex physics-based approach.\n\n    Args:\n        flare_roi (tuple[float, float, float, float]): Region of interest where the sun flare\n            can appear. Values are in the range [0, 1] and represent (x_min, y_min, x_max, y_max)\n            in relative coordinates. Default: (0, 0, 1, 0.5).\n        angle_range (tuple[float, float]): Range of angles (in radians) for the flare direction.\n            Values should be in the range [0, 1], where 0 represents 0 radians and 1 represents 2\u03c0 radians.\n            Default: (0, 1).\n        num_flare_circles_range (tuple[int, int]): Range for the number of flare circles to generate.\n            Default: (6, 10).\n        src_radius (int): Radius of the sun circle in pixels. Default: 400.\n        src_color (tuple[int, int, int]): Color of the sun in RGB format. Default: (255, 255, 255).\n        method (Literal[\"overlay\", \"physics_based\"]): Method to use for generating the sun flare.\n            \"overlay\" uses a simple alpha blending technique, while \"physics_based\" simulates\n            more realistic optical phenomena. Default: \"physics_based\".\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        The transform offers two methods for generating sun flares:\n\n        1. Overlay Method (\"overlay\"):\n           - Creates a simple sun flare effect using basic alpha blending.\n           - Steps:\n             a. Generate the main sun circle with a radial gradient.\n             b. Create smaller flare circles along the flare line.\n             c. Blend these elements with the original image using alpha compositing.\n           - Characteristics:\n             * Faster computation\n             * Less realistic appearance\n             * Suitable for basic augmentation or when performance is a priority\n\n        2. Physics-based Method (\"physics_based\"):\n           - Simulates more realistic optical phenomena observed in actual lens flares.\n           - Steps:\n             a. Create a separate flare layer for complex manipulations.\n             b. Add the main sun circle and diffraction spikes to simulate light diffraction.\n             c. Generate and add multiple flare circles with varying properties.\n             d. Apply Gaussian blur to create a soft, glowing effect.\n             e. Create and apply a radial gradient mask for natural fading from the center.\n             f. Simulate chromatic aberration by applying different blurs to color channels.\n             g. Blend the flare with the original image using screen blending mode.\n           - Characteristics:\n             * More computationally intensive\n             * Produces more realistic and visually appealing results\n             * Includes effects like diffraction spikes and chromatic aberration\n             * Suitable for high-quality augmentation or realistic image synthesis\n\n    Mathematical Formulation:\n        For both methods:\n        1. Sun position (x_s, y_s) is randomly chosen within the specified ROI.\n        2. Flare angle \u03b8 is randomly chosen from the angle_range.\n        3. For each flare circle i:\n           - Position (x_i, y_i) = (x_s + t_i * cos(\u03b8), y_s + t_i * sin(\u03b8))\n             where t_i is a random distance along the flare line.\n           - Radius r_i is randomly chosen, with larger circles closer to the sun.\n           - Alpha (transparency) alpha_i is randomly chosen in the range [0.05, 0.2].\n           - Color (R_i, G_i, B_i) is randomly chosen close to src_color.\n\n        Overlay method blending:\n        new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\n        Physics-based method blending:\n        new_pixel = 255 - ((255 - original_pixel) * (255 - flare_pixel) / 255)\n\n        4. Each flare circle is blended with the image using alpha compositing:\n           new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n\n        # Default sun flare (overlay method)\n        >>> transform = A.RandomSunFlare(p=1.0)\n        >>> flared_image = transform(image=image)[\"image\"]\n\n        # Physics-based sun flare with custom parameters\n\n        # Default sun flare\n        >>> transform = A.RandomSunFlare(p=1.0)\n        >>> flared_image = transform(image=image)[\"image\"]\n\n        # Custom sun flare parameters\n\n        >>> transform = A.RandomSunFlare(\n        ...     flare_roi=(0.1, 0, 0.9, 0.3),\n        ...     angle_range=(0.25, 0.75),\n        ...     num_flare_circles_range=(5, 15),\n        ...     src_radius=200,\n        ...     src_color=(255, 200, 100),\n        ...     method=\"physics_based\",\n        ...     p=1.0\n        ... )\n        >>> flared_image = transform(image=image)[\"image\"]\n\n    References:\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Diffraction: https://en.wikipedia.org/wiki/Diffraction\n        - Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration\n        - Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        flare_roi: tuple[float, float, float, float]\n        angle_lower: float | None = Field(ge=0, le=1)\n        angle_upper: float | None = Field(ge=0, le=1)\n\n        num_flare_circles_lower: int | None = Field(\n            ge=0,\n        )\n        num_flare_circles_upper: int | None = Field(\n            gt=0,\n        )\n        src_radius: int = Field(gt=1)\n        src_color: tuple[int, ...]\n\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        num_flare_circles_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n        method: Literal[\"overlay\", \"physics_based\"]\n\n        @model_validator(mode=\"after\")\n        def validate_parameters(self) -> Self:\n            (\n                flare_center_lower_x,\n                flare_center_lower_y,\n                flare_center_upper_x,\n                flare_center_upper_y,\n            ) = self.flare_roi\n            if (\n                not 0 <= flare_center_lower_x < flare_center_upper_x <= 1\n                or not 0 <= flare_center_lower_y < flare_center_upper_y <= 1\n            ):\n                raise ValueError(f\"Invalid flare_roi. Got: {self.flare_roi}\")\n\n            if self.angle_lower is not None or self.angle_upper is not None:\n                if self.angle_lower is not None:\n                    warn(\n                        \"`angle_lower` deprecated. Use `angle_range` as tuple (angle_lower, angle_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.angle_upper is not None:\n                    warn(\n                        \"`angle_upper` deprecated. Use `angle_range` as tuple(angle_lower, angle_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.angle_lower if self.angle_lower is not None else self.angle_range[0]\n                upper = self.angle_upper if self.angle_upper is not None else self.angle_range[1]\n                self.angle_range = (lower, upper)\n\n            if self.num_flare_circles_lower is not None or self.num_flare_circles_upper is not None:\n                if self.num_flare_circles_lower is not None:\n                    warn(\n                        \"`num_flare_circles_lower` deprecated. Use `num_flare_circles_range` as tuple\"\n                        \" (num_flare_circles_lower, num_flare_circles_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.num_flare_circles_upper is not None:\n                    warn(\n                        \"`num_flare_circles_upper` deprecated. Use `num_flare_circles_range` as tuple\"\n                        \" (num_flare_circles_lower, num_flare_circles_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = (\n                    self.num_flare_circles_lower\n                    if self.num_flare_circles_lower is not None\n                    else self.num_flare_circles_range[0]\n                )\n                upper = (\n                    self.num_flare_circles_upper\n                    if self.num_flare_circles_upper is not None\n                    else self.num_flare_circles_range[1]\n                )\n                self.num_flare_circles_range = (lower, upper)\n\n            return self\n\n    def __init__(\n        self,\n        flare_roi: tuple[float, float, float, float] = (0, 0, 1, 0.5),\n        angle_lower: float | None = None,\n        angle_upper: float | None = None,\n        num_flare_circles_lower: int | None = None,\n        num_flare_circles_upper: int | None = None,\n        src_radius: int = 400,\n        src_color: tuple[int, ...] = (255, 255, 255),\n        angle_range: tuple[float, float] = (0, 1),\n        num_flare_circles_range: tuple[int, int] = (6, 10),\n        method: Literal[\"overlay\", \"physics_based\"] = \"overlay\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.angle_range = angle_range\n        self.num_flare_circles_range = num_flare_circles_range\n\n        self.src_radius = src_radius\n        self.src_color = src_color\n        self.flare_roi = flare_roi\n        self.method = method\n\n    def apply(\n        self,\n        img: np.ndarray,\n        flare_center: tuple[float, float],\n        circles: list[Any],\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        if self.method == \"overlay\":\n            return fmain.add_sun_flare_overlay(\n                img,\n                flare_center,\n                self.src_radius,\n                self.src_color,\n                circles,\n            )\n        if self.method == \"physics_based\":\n            return fmain.add_sun_flare_physics_based(\n                img,\n                flare_center,\n                self.src_radius,\n                self.src_color,\n                circles,\n            )\n\n        raise ValueError(f\"Invalid method: {self.method}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        diagonal = math.sqrt(height**2 + width**2)\n\n        angle = 2 * math.pi * self.py_random.uniform(*self.angle_range)\n\n        # Calculate flare center in pixel coordinates\n        x_min, y_min, x_max, y_max = self.flare_roi\n        flare_center_x = int(width * self.py_random.uniform(x_min, x_max))\n        flare_center_y = int(height * self.py_random.uniform(y_min, y_max))\n\n        num_circles = self.py_random.randint(*self.num_flare_circles_range)\n\n        # Calculate parameters relative to image size\n        step_size = max(1, int(diagonal * 0.01))  # 1% of diagonal, minimum 1 pixel\n        max_radius = max(2, int(height * 0.01))  # 1% of height, minimum 2 pixels\n        color_range = int(max(self.src_color) * 0.2)  # 20% of max color value\n\n        def line(t: float) -> tuple[float, float]:\n            return (\n                flare_center_x + t * math.cos(angle),\n                flare_center_y + t * math.sin(angle),\n            )\n\n        # Generate points along the flare line\n        t_range = range(-flare_center_x, width - flare_center_x, step_size)\n        points = [line(t) for t in t_range]\n\n        circles = []\n        for _ in range(num_circles):\n            alpha = self.py_random.uniform(0.05, 0.2)\n            point = self.py_random.choice(points)\n            rad = self.py_random.randint(1, max_radius)\n\n            # Generate colors relative to src_color\n            colors = [self.py_random.randint(max(c - color_range, 0), c) for c in self.src_color]\n\n            circles.append(\n                (\n                    alpha,\n                    (int(point[0]), int(point[1])),\n                    pow(rad, 3),\n                    tuple(colors),\n                ),\n            )\n\n        return {\n            \"circles\": circles,\n            \"flare_center\": (flare_center_x, flare_center_y),\n        }\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"flare_roi\": self.flare_roi,\n            \"angle_range\": self.angle_range,\n            \"num_flare_circles_range\": self.num_flare_circles_range,\n            \"src_radius\": self.src_radius,\n            \"src_color\": self.src_color,\n        }\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomToneCurve","title":"class RandomToneCurve (scale=0.1, per_channel=False, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly change the relationship between bright and dark areas of the image by manipulating its tone curve.

This transform applies a random S-curve to the image's tone curve, adjusting the brightness and contrast in a non-linear manner. It can be applied to the entire image or to each channel separately.

Parameters:

Name Type Description scale float

Standard deviation of the normal distribution used to sample random distances to move two control points that modify the image's curve. Values should be in range [0, 1]. Higher values will result in more dramatic changes to the image. Default: 0.1

per_channel bool

If True, the tone curve will be applied to each channel of the input image separately, which can lead to color distortion. If False, the same curve is applied to all channels, preserving the original color relationships. Default: False

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform modifies the image's histogram by applying a smooth, S-shaped curve to it.
  • The S-curve is defined by moving two control points of a quadratic B\u00e9zier curve.
  • When per_channel is False, the same curve is applied to all channels, maintaining color balance.
  • When per_channel is True, different curves are applied to each channel, which can create color shifts.
  • This transform can be used to adjust image contrast and brightness in a more natural way than linear transforms.
  • The effect can range from subtle contrast adjustments to more dramatic \"vintage\" or \"faded\" looks.

Mathematical Formulation: 1. Two control points are randomly moved from their default positions (0.25, 0.25) and (0.75, 0.75). 2. The new positions are sampled from a normal distribution: N(\u03bc, \u03c3\u00b2), where \u03bc is the original position and alpha is the scale parameter. 3. These points, along with fixed points at (0, 0) and (1, 1), define a quadratic B\u00e9zier curve. 4. The curve is applied as a lookup table to the image intensities: new_intensity = curve(original_intensity)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-a-random-tone-curve-to-all-channels-together","title":"Apply a random tone curve to all channels together","text":"Python
>>> transform = A.RandomToneCurve(scale=0.1, per_channel=False, p=1.0)\n>>> augmented_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-random-tone-curves-to-each-channel-separately","title":"Apply random tone curves to each channel separately","text":"Python
>>> transform = A.RandomToneCurve(scale=0.2, per_channel=True, p=1.0)\n>>> augmented_image = transform(image=image)['image']\n

References

  • \"What Else Can Fool Deep Learning? Addressing Color Constancy Errors on Deep Neural Network Performance\" by Mahmoud Afifi and Michael S. Brown, ICCV 2019.
  • B\u00e9zier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B%C3%A9zier_curves
  • Tone mapping: https://en.wikipedia.org/wiki/Tone_mapping

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomToneCurve(ImageOnlyTransform):\n    \"\"\"Randomly change the relationship between bright and dark areas of the image by manipulating its tone curve.\n\n    This transform applies a random S-curve to the image's tone curve, adjusting the brightness and contrast\n    in a non-linear manner. It can be applied to the entire image or to each channel separately.\n\n    Args:\n        scale (float): Standard deviation of the normal distribution used to sample random distances\n            to move two control points that modify the image's curve. Values should be in range [0, 1].\n            Higher values will result in more dramatic changes to the image. Default: 0.1\n        per_channel (bool): If True, the tone curve will be applied to each channel of the input image separately,\n            which can lead to color distortion. If False, the same curve is applied to all channels,\n            preserving the original color relationships. Default: False\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform modifies the image's histogram by applying a smooth, S-shaped curve to it.\n        - The S-curve is defined by moving two control points of a quadratic B\u00e9zier curve.\n        - When per_channel is False, the same curve is applied to all channels, maintaining color balance.\n        - When per_channel is True, different curves are applied to each channel, which can create color shifts.\n        - This transform can be used to adjust image contrast and brightness in a more natural way than linear\n            transforms.\n        - The effect can range from subtle contrast adjustments to more dramatic \"vintage\" or \"faded\" looks.\n\n    Mathematical Formulation:\n        1. Two control points are randomly moved from their default positions (0.25, 0.25) and (0.75, 0.75).\n        2. The new positions are sampled from a normal distribution: N(\u03bc, \u03c3\u00b2), where \u03bc is the original position\n        and alpha is the scale parameter.\n        3. These points, along with fixed points at (0, 0) and (1, 1), define a quadratic B\u00e9zier curve.\n        4. The curve is applied as a lookup table to the image intensities:\n           new_intensity = curve(original_intensity)\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply a random tone curve to all channels together\n        >>> transform = A.RandomToneCurve(scale=0.1, per_channel=False, p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n        # Apply random tone curves to each channel separately\n        >>> transform = A.RandomToneCurve(scale=0.2, per_channel=True, p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n    References:\n        - \"What Else Can Fool Deep Learning? Addressing Color Constancy Errors on Deep Neural Network Performance\"\n          by Mahmoud Afifi and Michael S. Brown, ICCV 2019.\n        - B\u00e9zier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B%C3%A9zier_curves\n        - Tone mapping: https://en.wikipedia.org/wiki/Tone_mapping\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: float = Field(\n            ge=0,\n            le=1,\n        )\n        per_channel: bool\n\n    def __init__(\n        self,\n        scale: float = 0.1,\n        per_channel: bool = False,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale = scale\n        self.per_channel = per_channel\n\n    def apply(\n        self,\n        img: np.ndarray,\n        low_y: float | np.ndarray,\n        high_y: float | np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.move_tone_curve(img, low_y, high_y)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if self.per_channel and num_channels != 1:\n            return {\n                \"low_y\": np.clip(\n                    self.random_generator.normal(\n                        loc=0.25,\n                        scale=self.scale,\n                        size=(num_channels,),\n                    ),\n                    0,\n                    1,\n                ),\n                \"high_y\": np.clip(\n                    self.random_generator.normal(\n                        loc=0.75,\n                        scale=self.scale,\n                        size=(num_channels,),\n                    ),\n                    0,\n                    1,\n                ),\n            }\n        # Same values for all channels\n        low_y = np.clip(self.random_generator.normal(loc=0.25, scale=self.scale), 0, 1)\n        high_y = np.clip(self.random_generator.normal(loc=0.75, scale=self.scale), 0, 1)\n\n        return {\"low_y\": low_y, \"high_y\": high_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"scale\", \"per_channel\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RingingOvershoot","title":"class RingingOvershoot (blur_limit=(7, 15), cutoff=(0.7853981633974483, 1.5707963267948966), p=0.5, always_apply=None) [view source on GitHub]","text":"

Create ringing or overshoot artifacts by convolving the image with a 2D sinc filter.

This transform simulates the ringing artifacts that can occur in digital image processing, particularly after sharpening or edge enhancement operations. It creates oscillations or overshoots near sharp transitions in the image.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Maximum kernel size for the sinc filter. Must be an odd number in the range [3, inf). If a single int is provided, the kernel size will be randomly chosen from the range (3, blur_limit). If a tuple (min, max) is provided, the kernel size will be randomly chosen from the range (min, max). Default: (7, 15).

cutoff tuple[float, float]

Range to choose the cutoff frequency in radians. Values should be in the range (0, \u03c0). A lower cutoff frequency will result in more pronounced ringing effects. Default: (\u03c0/4, \u03c0/2).

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • Ringing artifacts are oscillations of the image intensity function in the neighborhood of sharp transitions, such as edges or object boundaries.
  • This transform uses a 2D sinc filter (also known as a 2D cardinal sine function) to introduce these artifacts.
  • The severity of the ringing effect is controlled by both the kernel size (blur_limit) and the cutoff frequency.
  • Larger kernel sizes and lower cutoff frequencies will generally produce more noticeable ringing effects.
  • This transform can be useful for:
  • Simulating imperfections in image processing or transmission systems
  • Testing the robustness of computer vision models to ringing artifacts
  • Creating artistic effects that emphasize edges and transitions in images

Mathematical Formulation: The 2D sinc filter kernel is defined as:

K(x, y) = cutoff * J\u2081(cutoff * \u221a(x\u00b2 + y\u00b2)) / (2\u03c0 * \u221a(x\u00b2 + y\u00b2))\n\nwhere:\n- J\u2081 is the Bessel function of the first kind of order 1\n- cutoff is the chosen cutoff frequency\n- x and y are the distances from the kernel center\n\nThe filtered image I' is obtained by convolving the input image I with the kernel K:\n\nI'(x, y) = \u2211\u2211 I(x-u, y-v) * K(u, v)\n\nThe convolution operation introduces the ringing artifacts near sharp transitions.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-ringing-effect-with-default-parameters","title":"Apply ringing effect with default parameters","text":"Python
>>> transform = A.RingingOvershoot(p=1.0)\n>>> ringing_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-ringing-effect-with-custom-parameters","title":"Apply ringing effect with custom parameters","text":"Python
>>> transform = A.RingingOvershoot(\n...     blur_limit=(9, 17),\n...     cutoff=(np.pi/6, np.pi/3),\n...     p=1.0\n... )\n>>> ringing_image = transform(image=image)['image']\n

References

  • Ringing artifacts: https://en.wikipedia.org/wiki/Ringing_artifacts
  • Sinc filter: https://en.wikipedia.org/wiki/Sinc_filter
  • \"The Importance of Ringing Artifacts in Image Processing\" by Jae S. Lim, 1981
  • \"Digital Image Processing\" by Rafael C. Gonzalez and Richard E. Woods, 4th Edition

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RingingOvershoot(ImageOnlyTransform):\n    \"\"\"Create ringing or overshoot artifacts by convolving the image with a 2D sinc filter.\n\n    This transform simulates the ringing artifacts that can occur in digital image processing,\n    particularly after sharpening or edge enhancement operations. It creates oscillations\n    or overshoots near sharp transitions in the image.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Maximum kernel size for the sinc filter.\n            Must be an odd number in the range [3, inf).\n            If a single int is provided, the kernel size will be randomly chosen\n            from the range (3, blur_limit). If a tuple (min, max) is provided,\n            the kernel size will be randomly chosen from the range (min, max).\n            Default: (7, 15).\n        cutoff (tuple[float, float]): Range to choose the cutoff frequency in radians.\n            Values should be in the range (0, \u03c0). A lower cutoff frequency will\n            result in more pronounced ringing effects.\n            Default: (\u03c0/4, \u03c0/2).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - Ringing artifacts are oscillations of the image intensity function in the neighborhood\n          of sharp transitions, such as edges or object boundaries.\n        - This transform uses a 2D sinc filter (also known as a 2D cardinal sine function)\n          to introduce these artifacts.\n        - The severity of the ringing effect is controlled by both the kernel size (blur_limit)\n          and the cutoff frequency.\n        - Larger kernel sizes and lower cutoff frequencies will generally produce more\n          noticeable ringing effects.\n        - This transform can be useful for:\n          * Simulating imperfections in image processing or transmission systems\n          * Testing the robustness of computer vision models to ringing artifacts\n          * Creating artistic effects that emphasize edges and transitions in images\n\n    Mathematical Formulation:\n        The 2D sinc filter kernel is defined as:\n\n        K(x, y) = cutoff * J\u2081(cutoff * \u221a(x\u00b2 + y\u00b2)) / (2\u03c0 * \u221a(x\u00b2 + y\u00b2))\n\n        where:\n        - J\u2081 is the Bessel function of the first kind of order 1\n        - cutoff is the chosen cutoff frequency\n        - x and y are the distances from the kernel center\n\n        The filtered image I' is obtained by convolving the input image I with the kernel K:\n\n        I'(x, y) = \u2211\u2211 I(x-u, y-v) * K(u, v)\n\n        The convolution operation introduces the ringing artifacts near sharp transitions.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Apply ringing effect with default parameters\n        >>> transform = A.RingingOvershoot(p=1.0)\n        >>> ringing_image = transform(image=image)['image']\n\n        # Apply ringing effect with custom parameters\n        >>> transform = A.RingingOvershoot(\n        ...     blur_limit=(9, 17),\n        ...     cutoff=(np.pi/6, np.pi/3),\n        ...     p=1.0\n        ... )\n        >>> ringing_image = transform(image=image)['image']\n\n    References:\n        - Ringing artifacts: https://en.wikipedia.org/wiki/Ringing_artifacts\n        - Sinc filter: https://en.wikipedia.org/wiki/Sinc_filter\n        - \"The Importance of Ringing Artifacts in Image Processing\" by Jae S. Lim, 1981\n        - \"Digital Image Processing\" by Rafael C. Gonzalez and Richard E. Woods, 4th Edition\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        blur_limit: ScaleIntType\n        cutoff: Annotated[tuple[float, float], nondecreasing]\n\n        @field_validator(\"cutoff\")\n        @classmethod\n        def check_cutoff(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 0, np.pi\n            check_range(v, *bounds, info.field_name)\n            return v\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (7, 15),\n        cutoff: tuple[float, float] = (np.pi / 4, np.pi / 2),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.cutoff = cutoff\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        ksize = self.py_random.randrange(self.blur_limit[0], self.blur_limit[1] + 1, 2)\n        if ksize % 2 == 0:\n            raise ValueError(f\"Kernel size must be odd. Got: {ksize}\")\n\n        cutoff = self.py_random.uniform(*self.cutoff)\n\n        # From dsp.stackexchange.com/questions/58301/2-d-circularly-symmetric-low-pass-filter\n        with np.errstate(divide=\"ignore\", invalid=\"ignore\"):\n            kernel = np.fromfunction(\n                lambda x, y: cutoff\n                * special.j1(\n                    cutoff * np.sqrt((x - (ksize - 1) / 2) ** 2 + (y - (ksize - 1) / 2) ** 2),\n                )\n                / (2 * np.pi * np.sqrt((x - (ksize - 1) / 2) ** 2 + (y - (ksize - 1) / 2) ** 2)),\n                [ksize, ksize],\n            )\n        kernel[(ksize - 1) // 2, (ksize - 1) // 2] = cutoff**2 / (4 * np.pi)\n\n        # Normalize kernel\n        kernel = kernel.astype(np.float32) / np.sum(kernel)\n\n        return {\"kernel\": kernel}\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel)\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"blur_limit\", \"cutoff\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.SaltAndPepper","title":"class SaltAndPepper (amount=(0.01, 0.06), salt_vs_pepper=(0.4, 0.6), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply salt and pepper noise to the input image.

Salt and pepper noise is a form of impulse noise that randomly sets pixels to either maximum value (salt) or minimum value (pepper). The amount and proportion of salt vs pepper noise can be controlled.

Parameters:

Name Type Description amount float, float

Range for total amount of noise (both salt and pepper). Values between 0 and 1. For example: - 0.05 means 5% of all pixels will be replaced with noise - (0.01, 0.06) will sample amount uniformly from 1% to 6% Default: (0.01, 0.06)

salt_vs_pepper float, float

Range for ratio of salt (white) vs pepper (black) noise. Values between 0 and 1. For example: - 0.5 means equal amounts of salt and pepper - 0.7 means 70% of noisy pixels will be salt, 30% pepper - (0.4, 0.6) will sample ratio uniformly from 40% to 60% Default: (0.4, 0.6)

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • Salt noise sets pixels to maximum value (255 for uint8, 1.0 for float32)
  • Pepper noise sets pixels to 0
  • Salt and pepper masks are generated independently, so a pixel could theoretically be selected for both (in this case, pepper overrides salt)
  • The actual number of affected pixels might slightly differ from the specified amount due to random sampling and potential overlap of salt and pepper masks

Mathematical Formulation: For an input image I, the output O is: O[x,y] = max_value, if salt_mask[x,y] = True O[x,y] = 0, if pepper_mask[x,y] = True O[x,y] = I[x,y], otherwise

where:\nP(salt_mask[x,y] = True) = amount * salt_ratio\nP(pepper_mask[x,y] = True) = amount * (1 - salt_ratio)\namount \u2208 [amount_min, amount_max]\nsalt_ratio \u2208 [salt_vs_pepper_min, salt_vs_pepper_max]\n

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-salt-and-pepper-noise-with-default-parameters","title":"Apply salt and pepper noise with default parameters","text":"Python
>>> transform = A.SaltAndPepper(p=1.0)\n>>> noisy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--heavy-noise-with-more-salt-than-pepper","title":"Heavy noise with more salt than pepper","text":"Python
>>> transform = A.SaltAndPepper(\n...     amount=(0.1, 0.2),       # 10-20% of pixels will be noisy\n...     salt_vs_pepper=(0.7, 0.9),  # 70-90% of noise will be salt\n...     p=1.0\n... )\n>>> noisy_image = transform(image=image)[\"image\"]\n

References

.. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\" Chapter 5: Image Restoration and Reconstruction.

.. [2] A. K. Jain, \"Fundamentals of Digital Image Processing,\" Chapter 7: Image Degradation and Restoration.

.. [3] Salt and pepper noise: https://en.wikipedia.org/wiki/Salt-and-pepper_noise

See Also: - GaussNoise: For additive Gaussian noise - MultiplicativeNoise: For multiplicative noise - ISONoise: For camera sensor noise simulation

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class SaltAndPepper(ImageOnlyTransform):\n    \"\"\"Apply salt and pepper noise to the input image.\n\n    Salt and pepper noise is a form of impulse noise that randomly sets pixels to either maximum value (salt)\n    or minimum value (pepper). The amount and proportion of salt vs pepper noise can be controlled.\n\n    Args:\n        amount ((float, float)): Range for total amount of noise (both salt and pepper).\n            Values between 0 and 1. For example:\n            - 0.05 means 5% of all pixels will be replaced with noise\n            - (0.01, 0.06) will sample amount uniformly from 1% to 6%\n            Default: (0.01, 0.06)\n\n        salt_vs_pepper ((float, float)): Range for ratio of salt (white) vs pepper (black) noise.\n            Values between 0 and 1. For example:\n            - 0.5 means equal amounts of salt and pepper\n            - 0.7 means 70% of noisy pixels will be salt, 30% pepper\n            - (0.4, 0.6) will sample ratio uniformly from 40% to 60%\n            Default: (0.4, 0.6)\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Salt noise sets pixels to maximum value (255 for uint8, 1.0 for float32)\n        - Pepper noise sets pixels to 0\n        - Salt and pepper masks are generated independently, so a pixel could theoretically\n          be selected for both (in this case, pepper overrides salt)\n        - The actual number of affected pixels might slightly differ from the specified amount\n          due to random sampling and potential overlap of salt and pepper masks\n\n    Mathematical Formulation:\n        For an input image I, the output O is:\n        O[x,y] = max_value,  if salt_mask[x,y] = True\n        O[x,y] = 0,         if pepper_mask[x,y] = True\n        O[x,y] = I[x,y],    otherwise\n\n        where:\n        P(salt_mask[x,y] = True) = amount * salt_ratio\n        P(pepper_mask[x,y] = True) = amount * (1 - salt_ratio)\n        amount \u2208 [amount_min, amount_max]\n        salt_ratio \u2208 [salt_vs_pepper_min, salt_vs_pepper_max]\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Apply salt and pepper noise with default parameters\n        >>> transform = A.SaltAndPepper(p=1.0)\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n        # Heavy noise with more salt than pepper\n        >>> transform = A.SaltAndPepper(\n        ...     amount=(0.1, 0.2),       # 10-20% of pixels will be noisy\n        ...     salt_vs_pepper=(0.7, 0.9),  # 70-90% of noise will be salt\n        ...     p=1.0\n        ... )\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n    References:\n        .. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\"\n               Chapter 5: Image Restoration and Reconstruction.\n\n        .. [2] A. K. Jain, \"Fundamentals of Digital Image Processing,\"\n               Chapter 7: Image Degradation and Restoration.\n\n        .. [3] Salt and pepper noise:\n               https://en.wikipedia.org/wiki/Salt-and-pepper_noise\n\n    See Also:\n        - GaussNoise: For additive Gaussian noise\n        - MultiplicativeNoise: For multiplicative noise\n        - ISONoise: For camera sensor noise simulation\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        amount: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        salt_vs_pepper: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n\n    def __init__(\n        self,\n        amount: tuple[float, float] = (0.01, 0.06),\n        salt_vs_pepper: tuple[float, float] = (0.4, 0.6),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.amount = amount\n        self.salt_vs_pepper = salt_vs_pepper\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample total amount and salt ratio\n        total_amount = self.py_random.uniform(*self.amount)\n        salt_ratio = self.py_random.uniform(*self.salt_vs_pepper)\n\n        # Calculate individual probabilities\n        prob_salt = total_amount * salt_ratio\n        prob_pepper = total_amount * (1 - salt_ratio)\n\n        # Generate masks\n        salt_mask = self.random_generator.random(image.shape) < prob_salt\n        pepper_mask = self.random_generator.random(image.shape) < prob_pepper\n\n        return {\n            \"salt_mask\": salt_mask,\n            \"pepper_mask\": pepper_mask,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        salt_mask: np.ndarray,\n        pepper_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_salt_and_pepper(img, salt_mask, pepper_mask)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"amount\", \"salt_vs_pepper\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Sharpen","title":"class Sharpen (alpha=(0.2, 0.5), lightness=(0.5, 1.0), method='kernel', kernel_size=5, sigma=1.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Sharpen the input image using either kernel-based or Gaussian interpolation method.

Implements two different approaches to image sharpening: 1. Traditional kernel-based method using Laplacian operator 2. Gaussian interpolation method (similar to Kornia's approach)

Parameters:

Name Type Description alpha tuple[float, float]

Range for the visibility of sharpening effect. At 0, only the original image is visible, at 1.0 only its processed version is visible. Values should be in the range [0, 1]. Used in both methods. Default: (0.2, 0.5).

lightness tuple[float, float]

Range for the lightness of the sharpened image. Only used in 'kernel' method. Larger values create higher contrast. Values should be greater than 0. Default: (0.5, 1.0).

method Literal['kernel', 'gaussian']

Sharpening algorithm to use: - 'kernel': Traditional kernel-based sharpening using Laplacian operator - 'gaussian': Interpolation between Gaussian blurred and original image Default: 'kernel'

kernel_size int

Size of the Gaussian blur kernel for 'gaussian' method. Must be odd. Default: 5

sigma float

Standard deviation for Gaussian kernel in 'gaussian' method. Default: 1.0

p float

Probability of applying the transform. Default: 0.5.

Image types: uint8, float32

Number of channels: Any

Mathematical Formulation: 1. Kernel Method: The sharpening operation is based on the Laplacian operator L: L = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]

   The final kernel K is a weighted sum:\n   K = (1 - a)I + a(L + \u03bbI)\n\n   where:\n   - a is the alpha value\n   - \u03bb is the lightness value\n   - I is the identity kernel\n\n   The output image O is computed as:\n   O = K * I  (convolution)\n\n2. Gaussian Method:\n   Based on the unsharp mask principle:\n   O = aI + (1-a)G\n\n   where:\n   - I is the input image\n   - G is the Gaussian blurred version of I\n   - a is the alpha value (sharpness)\n\n   The Gaussian kernel G(x,y) is defined as:\n   G(x,y) = (1/(2\u03c0s\u00b2))exp(-(x\u00b2+y\u00b2)/(2s\u00b2))\n

Note

  • Kernel sizes must be odd to maintain spatial alignment
  • Methods produce different visual results:
  • Kernel method: More pronounced edges, possible artifacts
  • Gaussian method: More natural look, limited to original sharpness

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--traditional-kernel-sharpening","title":"Traditional kernel sharpening","text":"Python
>>> transform = A.Sharpen(\n...     alpha=(0.2, 0.5),\n...     lightness=(0.5, 1.0),\n...     method='kernel',\n...     p=1.0\n... )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--gaussian-interpolation-sharpening","title":"Gaussian interpolation sharpening","text":"Python
>>> transform = A.Sharpen(\n...     alpha=(0.5, 1.0),\n...     method='gaussian',\n...     kernel_size=5,\n...     sigma=1.0,\n...     p=1.0\n... )\n

References

.. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\" Chapter 3: Intensity Transformations and Spatial Filtering.

.. [2] J. C. Russ, \"The Image Processing Handbook (7th Edition),\" Chapter 4: Image Enhancement.

.. [3] T. Acharya and A. K. Ray, \"Image Processing: Principles and Applications,\" Chapter 5: Image Enhancement.

.. [4] Unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking

.. [5] Laplacian operator: https://en.wikipedia.org/wiki/Laplace_operator

.. [6] Gaussian blur: https://en.wikipedia.org/wiki/Gaussian_blur

See Also: - Blur: For Gaussian blurring - UnsharpMask: Alternative sharpening method - RandomBrightnessContrast: For adjusting image contrast

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Sharpen(ImageOnlyTransform):\n    \"\"\"Sharpen the input image using either kernel-based or Gaussian interpolation method.\n\n    Implements two different approaches to image sharpening:\n    1. Traditional kernel-based method using Laplacian operator\n    2. Gaussian interpolation method (similar to Kornia's approach)\n\n    Args:\n        alpha (tuple[float, float]): Range for the visibility of sharpening effect.\n            At 0, only the original image is visible, at 1.0 only its processed version is visible.\n            Values should be in the range [0, 1].\n            Used in both methods. Default: (0.2, 0.5).\n\n        lightness (tuple[float, float]): Range for the lightness of the sharpened image.\n            Only used in 'kernel' method. Larger values create higher contrast.\n            Values should be greater than 0. Default: (0.5, 1.0).\n\n        method (Literal['kernel', 'gaussian']): Sharpening algorithm to use:\n            - 'kernel': Traditional kernel-based sharpening using Laplacian operator\n            - 'gaussian': Interpolation between Gaussian blurred and original image\n            Default: 'kernel'\n\n        kernel_size (int): Size of the Gaussian blur kernel for 'gaussian' method.\n            Must be odd. Default: 5\n\n        sigma (float): Standard deviation for Gaussian kernel in 'gaussian' method.\n            Default: 1.0\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Mathematical Formulation:\n        1. Kernel Method:\n           The sharpening operation is based on the Laplacian operator L:\n           L = [[-1, -1, -1],\n                [-1,  8, -1],\n                [-1, -1, -1]]\n\n           The final kernel K is a weighted sum:\n           K = (1 - a)I + a(L + \u03bbI)\n\n           where:\n           - a is the alpha value\n           - \u03bb is the lightness value\n           - I is the identity kernel\n\n           The output image O is computed as:\n           O = K * I  (convolution)\n\n        2. Gaussian Method:\n           Based on the unsharp mask principle:\n           O = aI + (1-a)G\n\n           where:\n           - I is the input image\n           - G is the Gaussian blurred version of I\n           - a is the alpha value (sharpness)\n\n           The Gaussian kernel G(x,y) is defined as:\n           G(x,y) = (1/(2\u03c0s\u00b2))exp(-(x\u00b2+y\u00b2)/(2s\u00b2))\n\n    Note:\n        - Kernel sizes must be odd to maintain spatial alignment\n        - Methods produce different visual results:\n          * Kernel method: More pronounced edges, possible artifacts\n          * Gaussian method: More natural look, limited to original sharpness\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Traditional kernel sharpening\n        >>> transform = A.Sharpen(\n        ...     alpha=(0.2, 0.5),\n        ...     lightness=(0.5, 1.0),\n        ...     method='kernel',\n        ...     p=1.0\n        ... )\n\n        # Gaussian interpolation sharpening\n        >>> transform = A.Sharpen(\n        ...     alpha=(0.5, 1.0),\n        ...     method='gaussian',\n        ...     kernel_size=5,\n        ...     sigma=1.0,\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\"\n               Chapter 3: Intensity Transformations and Spatial Filtering.\n\n        .. [2] J. C. Russ, \"The Image Processing Handbook (7th Edition),\"\n               Chapter 4: Image Enhancement.\n\n        .. [3] T. Acharya and A. K. Ray, \"Image Processing: Principles and Applications,\"\n               Chapter 5: Image Enhancement.\n\n        .. [4] Unsharp masking:\n               https://en.wikipedia.org/wiki/Unsharp_masking\n\n        .. [5] Laplacian operator:\n               https://en.wikipedia.org/wiki/Laplace_operator\n\n        .. [6] Gaussian blur:\n               https://en.wikipedia.org/wiki/Gaussian_blur\n\n    See Also:\n        - Blur: For Gaussian blurring\n        - UnsharpMask: Alternative sharpening method\n        - RandomBrightnessContrast: For adjusting image contrast\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        lightness: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, None))]\n        method: Literal[\"kernel\", \"gaussian\"]\n        kernel_size: int = Field(ge=3)\n        sigma: float = Field(gt=0)\n\n    @field_validator(\"kernel_size\")\n    @classmethod\n    def check_kernel_size(cls, value: int) -> int:\n        return value + 1 if value % 2 == 0 else value\n\n    def __init__(\n        self,\n        alpha: tuple[float, float] = (0.2, 0.5),\n        lightness: tuple[float, float] = (0.5, 1.0),\n        method: Literal[\"kernel\", \"gaussian\"] = \"kernel\",\n        kernel_size: int = 5,\n        sigma: float = 1.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n        self.lightness = lightness\n        self.method = method\n        self.kernel_size = kernel_size\n        self.sigma = sigma\n\n    @staticmethod\n    def __generate_sharpening_matrix(\n        alpha: np.ndarray,\n        lightness: np.ndarray,\n    ) -> np.ndarray:\n        matrix_nochange = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=np.float32)\n        matrix_effect = np.array(\n            [[-1, -1, -1], [-1, 8 + lightness, -1], [-1, -1, -1]],\n            dtype=np.float32,\n        )\n\n        return (1 - alpha) * matrix_nochange + alpha * matrix_effect\n\n    def get_params(self) -> dict[str, Any]:\n        alpha = self.py_random.uniform(*self.alpha)\n\n        if self.method == \"kernel\":\n            lightness = self.py_random.uniform(*self.lightness)\n            return {\n                \"alpha\": alpha,\n                \"sharpening_matrix\": self.__generate_sharpening_matrix(\n                    alpha,\n                    lightness,\n                ),\n            }\n\n        return {\"alpha\": alpha, \"sharpening_matrix\": None}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha: float,\n        sharpening_matrix: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if self.method == \"kernel\":\n            return fmain.convolve(img, sharpening_matrix)\n        return fmain.sharpen_gaussian(img, alpha, self.kernel_size, self.sigma)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"alpha\", \"lightness\", \"method\", \"kernel_size\", \"sigma\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ShotNoise","title":"class ShotNoise (scale_range=(0.1, 0.3), p=0.5, always_apply=False) [view source on GitHub]","text":"

Apply shot noise to the image by modeling photon counting as a Poisson process.

Shot noise (also known as Poisson noise) occurs in imaging due to the quantum nature of light. When photons hit an imaging sensor, they arrive at random times following Poisson statistics. This transform simulates this physical process in linear light space by: 1. Converting to linear space (removing gamma) 2. Treating each pixel value as an expected photon count 3. Sampling actual photon counts from a Poisson distribution 4. Converting back to display space (reapplying gamma)

The noise characteristics follow real camera behavior: - Noise variance equals signal mean in linear space (Poisson statistics) - Brighter regions have more absolute noise but less relative noise - Darker regions have less absolute noise but more relative noise - Noise is generated independently for each pixel and color channel

Parameters:

Name Type Description scale_range tuple[float, float]

Range for sampling the noise scale factor. Represents the reciprocal of the expected photon count per unit intensity. Higher values mean more noise: - scale = 0.1: ~100 photons per unit intensity (low noise) - scale = 1.0: ~1 photon per unit intensity (moderate noise) - scale = 10.0: ~0.1 photons per unit intensity (high noise) Default: (0.1, 0.3)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • Performs calculations in linear light space (gamma = 2.2)
  • Preserves the image's mean intensity
  • Memory efficient with in-place operations
  • Thread-safe with independent random seeds

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Generate synthetic image\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> # Apply moderate shot noise\n>>> transform = A.ShotNoise(scale_range=(0.1, 1.0), p=1.0)\n>>> noisy_image = transform(image=image)[\"image\"]\n

References

  • Shot noise: https://en.wikipedia.org/wiki/Shot_noise
  • Original paper: https://doi.org/10.1002/andp.19183622304 (Schottky, 1918)
  • Poisson process: https://en.wikipedia.org/wiki/Poisson_point_process
  • Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ShotNoise(ImageOnlyTransform):\n    \"\"\"Apply shot noise to the image by modeling photon counting as a Poisson process.\n\n    Shot noise (also known as Poisson noise) occurs in imaging due to the quantum nature of light.\n    When photons hit an imaging sensor, they arrive at random times following Poisson statistics.\n    This transform simulates this physical process in linear light space by:\n    1. Converting to linear space (removing gamma)\n    2. Treating each pixel value as an expected photon count\n    3. Sampling actual photon counts from a Poisson distribution\n    4. Converting back to display space (reapplying gamma)\n\n    The noise characteristics follow real camera behavior:\n    - Noise variance equals signal mean in linear space (Poisson statistics)\n    - Brighter regions have more absolute noise but less relative noise\n    - Darker regions have less absolute noise but more relative noise\n    - Noise is generated independently for each pixel and color channel\n\n    Args:\n        scale_range (tuple[float, float]): Range for sampling the noise scale factor.\n            Represents the reciprocal of the expected photon count per unit intensity.\n            Higher values mean more noise:\n            - scale = 0.1: ~100 photons per unit intensity (low noise)\n            - scale = 1.0: ~1 photon per unit intensity (moderate noise)\n            - scale = 10.0: ~0.1 photons per unit intensity (high noise)\n            Default: (0.1, 0.3)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Performs calculations in linear light space (gamma = 2.2)\n        - Preserves the image's mean intensity\n        - Memory efficient with in-place operations\n        - Thread-safe with independent random seeds\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Generate synthetic image\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> # Apply moderate shot noise\n        >>> transform = A.ShotNoise(scale_range=(0.1, 1.0), p=1.0)\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Shot noise: https://en.wikipedia.org/wiki/Shot_noise\n        - Original paper: https://doi.org/10.1002/andp.19183622304 (Schottky, 1918)\n        - Poisson process: https://en.wikipedia.org/wiki/Poisson_point_process\n        - Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.1, 0.3),\n        p: float = 0.5,\n        always_apply: bool = False,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_range = scale_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        random_seed: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.shot_noise(img, scale, np.random.default_rng(random_seed))\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"scale\": self.py_random.uniform(*self.scale_range),\n            \"random_seed\": self.random_generator.integers(0, 2**32 - 1),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"scale_range\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Solarize","title":"class Solarize (threshold=None, threshold_range=(0.5, 0.5), p=0.5, always_apply=None) [view source on GitHub]","text":"

Invert all pixel values above a threshold.

This transform applies a solarization effect to the input image. Solarization is a phenomenon in photography in which the image recorded on a negative or on a photographic print is wholly or partially reversed in tone. Dark areas appear light or light areas appear dark.

In this implementation, all pixel values above a threshold are inverted.

Parameters:

Name Type Description threshold_range tuple[float, float]

Range for solarizing threshold as a fraction of maximum value. The threshold_range should be in the range [0, 1] and will be multiplied by the maximum value of the image type (255 for uint8 images or 1.0 for float images). Default: (0.5, 0.5) (corresponds to 127.5 for uint8 and 0.5 for float32).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • For uint8 images, pixel values above the threshold are inverted as: 255 - pixel_value
  • For float32 images, pixel values above the threshold are inverted as: 1.0 - pixel_value
  • The threshold is applied to each channel independently
  • The threshold is calculated in two steps:
  • Sample a value from threshold_range
  • Multiply by the image's maximum value:
    • For uint8: threshold = sampled_value * 255
    • For float32: threshold = sampled_value * 1.0
  • This transform can create interesting artistic effects or be used for data augmentation

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Solarize uint8 image with fixed threshold at 50% of max value (127.5)\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n>>>\n# Solarize uint8 image with random threshold between 40-60% of max value (102-153)\n>>> transform = A.Solarize(threshold_range=(0.4, 0.6), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n>>>\n# Solarize float32 image at 50% of max value (0.5)\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)\n>>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n

Mathematical Formulation: Let f be a value sampled from threshold_range (min, max). For each pixel value p: threshold = f * max_value if p > threshold: p_new = max_value - p !!! else p_new = p

Where max_value is 255 for uint8 images and 1.0 for float32 images.\n

See Also: Invert: For inverting all pixel values regardless of a threshold.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Solarize(ImageOnlyTransform):\n    \"\"\"Invert all pixel values above a threshold.\n\n    This transform applies a solarization effect to the input image. Solarization is a phenomenon in\n    photography in which the image recorded on a negative or on a photographic print is wholly or\n    partially reversed in tone. Dark areas appear light or light areas appear dark.\n\n    In this implementation, all pixel values above a threshold are inverted.\n\n    Args:\n        threshold_range (tuple[float, float]): Range for solarizing threshold as a fraction\n            of maximum value. The threshold_range should be in the range [0, 1] and will be multiplied by the\n            maximum value of the image type (255 for uint8 images or 1.0 for float images).\n            Default: (0.5, 0.5) (corresponds to 127.5 for uint8 and 0.5 for float32).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - For uint8 images, pixel values above the threshold are inverted as: 255 - pixel_value\n        - For float32 images, pixel values above the threshold are inverted as: 1.0 - pixel_value\n        - The threshold is applied to each channel independently\n        - The threshold is calculated in two steps:\n          1. Sample a value from threshold_range\n          2. Multiply by the image's maximum value:\n             * For uint8: threshold = sampled_value * 255\n             * For float32: threshold = sampled_value * 1.0\n        - This transform can create interesting artistic effects or be used for data augmentation\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Solarize uint8 image with fixed threshold at 50% of max value (127.5)\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n        >>>\n        # Solarize uint8 image with random threshold between 40-60% of max value (102-153)\n        >>> transform = A.Solarize(threshold_range=(0.4, 0.6), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n        >>>\n        # Solarize float32 image at 50% of max value (0.5)\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n\n    Mathematical Formulation:\n        Let f be a value sampled from threshold_range (min, max).\n        For each pixel value p:\n        threshold = f * max_value\n        if p > threshold:\n            p_new = max_value - p\n        else:\n            p_new = p\n\n        Where max_value is 255 for uint8 images and 1.0 for float32 images.\n\n    See Also:\n        Invert: For inverting all pixel values regardless of a threshold.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        threshold: ScaleFloatType | None\n        threshold_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @staticmethod\n        def normalize_threshold(\n            threshold: ScaleFloatType | None,\n            threshold_range: tuple[float, float],\n        ) -> tuple[float, float]:\n            \"\"\"Convert legacy threshold or use threshold_range, normalizing to [0,1] range.\"\"\"\n            if threshold is not None:\n                warn(\"`threshold` deprecated. Use `threshold_range` instead.\", DeprecationWarning, stacklevel=2)\n                value = to_tuple(threshold, threshold)\n                return (value[0] / 255, value[1] / 255) if value[1] > 1 else value\n            return threshold_range\n\n        @model_validator(mode=\"after\")\n        def process_threshold(self) -> Self:\n            self.threshold_range = self.normalize_threshold(\n                self.threshold,\n                self.threshold_range,\n            )\n            return self\n\n    def __init__(\n        self,\n        threshold: ScaleFloatType | None = None,\n        threshold_range: tuple[float, float] = (0.5, 0.5),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.threshold_range = threshold_range\n\n    def apply(self, img: np.ndarray, threshold: float, **params: Any) -> np.ndarray:\n        return fmain.solarize(img, threshold)\n\n    def get_params(self) -> dict[str, float]:\n        return {\"threshold\": self.py_random.uniform(*self.threshold_range)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"threshold_range\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Spatter","title":"class Spatter (mean=(0.65, 0.65), std=(0.3, 0.3), gauss_sigma=(2, 2), cutout_threshold=(0.68, 0.68), intensity=(0.6, 0.6), mode='rain', color=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply spatter transform. It simulates corruption which can occlude a lens in the form of rain or mud.

Parameters:

Name Type Description mean tuple[float, float] | float

Mean value of normal distribution for generating liquid layer. If single float mean will be sampled from (0, mean) If tuple of float mean will be sampled from range (mean[0], mean[1]). If you want constant value use (mean, mean). Default (0.65, 0.65)

std tuple[float, float] | float

Standard deviation value of normal distribution for generating liquid layer. If single float the number will be sampled from (0, std). If tuple of float std will be sampled from range (std[0], std[1]). If you want constant value use (std, std). Default: (0.3, 0.3).

gauss_sigma tuple[float, float] | floats

Sigma value for gaussian filtering of liquid layer. If single float the number will be sampled from (0, gauss_sigma). If tuple of float gauss_sigma will be sampled from range (gauss_sigma[0], gauss_sigma[1]). If you want constant value use (gauss_sigma, gauss_sigma). Default: (2, 3).

cutout_threshold tuple[float, float] | floats

Threshold for filtering liqued layer (determines number of drops). If single float it will used as cutout_threshold. If single float the number will be sampled from (0, cutout_threshold). If tuple of float cutout_threshold will be sampled from range (cutout_threshold[0], cutout_threshold[1]). If you want constant value use (cutout_threshold, cutout_threshold). Default: (0.68, 0.68).

intensity tuple[float, float] | floats

Intensity of corruption. If single float the number will be sampled from (0, intensity). If tuple of float intensity will be sampled from range (intensity[0], intensity[1]). If you want constant value use (intensity, intensity). Default: (0.6, 0.6).

mode str, or list[str]

Type of corruption. Currently, supported options are 'rain' and 'mud'. If list is provided type of corruption will be sampled list. Default: (\"rain\").

color list of (r, g, b) or dict or None

Corruption elements color. If list uses provided list as color for specified mode. If dict uses provided color for specified mode. Color for each specified mode should be provided in dict. If None uses default colors (rain: (238, 238, 175), mud: (20, 42, 63)).

p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Reference

https://arxiv.org/abs/1903.12261 https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Spatter(ImageOnlyTransform):\n    \"\"\"Apply spatter transform. It simulates corruption which can occlude a lens in the form of rain or mud.\n\n    Args:\n        mean (tuple[float, float] | float): Mean value of normal distribution for generating liquid layer.\n            If single float mean will be sampled from `(0, mean)`\n            If tuple of float mean will be sampled from range `(mean[0], mean[1])`.\n            If you want constant value use (mean, mean).\n            Default (0.65, 0.65)\n        std (tuple[float, float] | float): Standard deviation value of normal distribution for generating liquid layer.\n            If single float the number will be sampled from `(0, std)`.\n            If tuple of float std will be sampled from range `(std[0], std[1])`.\n            If you want constant value use (std, std).\n            Default: (0.3, 0.3).\n        gauss_sigma (tuple[float, float] | floats): Sigma value for gaussian filtering of liquid layer.\n            If single float the number will be sampled from `(0, gauss_sigma)`.\n            If tuple of float gauss_sigma will be sampled from range `(gauss_sigma[0], gauss_sigma[1])`.\n            If you want constant value use (gauss_sigma, gauss_sigma).\n            Default: (2, 3).\n        cutout_threshold (tuple[float, float] | floats): Threshold for filtering liqued layer\n            (determines number of drops). If single float it will used as cutout_threshold.\n            If single float the number will be sampled from `(0, cutout_threshold)`.\n            If tuple of float cutout_threshold will be sampled from range `(cutout_threshold[0], cutout_threshold[1])`.\n            If you want constant value use `(cutout_threshold, cutout_threshold)`.\n            Default: (0.68, 0.68).\n        intensity (tuple[float, float] | floats): Intensity of corruption.\n            If single float the number will be sampled from `(0, intensity)`.\n            If tuple of float intensity will be sampled from range `(intensity[0], intensity[1])`.\n            If you want constant value use `(intensity, intensity)`.\n            Default: (0.6, 0.6).\n        mode (str, or list[str]): Type of corruption. Currently, supported options are 'rain' and 'mud'.\n             If list is provided type of corruption will be sampled list. Default: (\"rain\").\n        color (list of (r, g, b) or dict or None): Corruption elements color.\n            If list uses provided list as color for specified mode.\n            If dict uses provided color for specified mode. Color for each specified mode should be provided in dict.\n            If None uses default colors (rain: (238, 238, 175), mud: (20, 42, 63)).\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://arxiv.org/abs/1903.12261\n        https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mean: ZeroOneRangeType = (0.65, 0.65)\n        std: ZeroOneRangeType = (0.3, 0.3)\n        gauss_sigma: NonNegativeFloatRangeType = (2, 2)\n        cutout_threshold: ZeroOneRangeType = (0.68, 0.68)\n        intensity: ZeroOneRangeType = (0.6, 0.6)\n        mode: SpatterMode | Sequence[SpatterMode]\n        color: Sequence[int] | dict[str, Sequence[int]] | None = None\n\n        @field_validator(\"mode\")\n        @classmethod\n        def check_mode(\n            cls,\n            mode: SpatterMode | Sequence[SpatterMode],\n        ) -> Sequence[SpatterMode]:\n            if isinstance(mode, str):\n                return [mode]\n            return mode\n\n        @model_validator(mode=\"after\")\n        def check_color(self) -> Self:\n            if self.color is None:\n                self.color = {\"rain\": [238, 238, 175], \"mud\": [20, 42, 63]}\n\n            elif isinstance(self.color, (list, tuple)) and len(self.mode) == 1:\n                if len(self.color) != NUM_RGB_CHANNELS:\n                    msg = \"Color must be a list of three integers for RGB format.\"\n                    raise ValueError(msg)\n                self.color = {self.mode[0]: self.color}\n            elif isinstance(self.color, dict):\n                result = {}\n                for mode in self.mode:\n                    if mode not in self.color:\n                        raise ValueError(f\"Color for mode {mode} is not specified.\")\n                    if len(self.color[mode]) != NUM_RGB_CHANNELS:\n                        raise ValueError(\n                            f\"Color for mode {mode} must be in RGB format.\",\n                        )\n                    result[mode] = self.color[mode]\n            else:\n                msg = \"Color must be a list of RGB values or a dict mapping mode to RGB values.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        mean: ScaleFloatType = (0.65, 0.65),\n        std: ScaleFloatType = (0.3, 0.3),\n        gauss_sigma: ScaleFloatType = (2, 2),\n        cutout_threshold: ScaleFloatType = (0.68, 0.68),\n        intensity: ScaleFloatType = (0.6, 0.6),\n        mode: SpatterMode | Sequence[SpatterMode] = \"rain\",\n        color: Sequence[int] | dict[str, Sequence[int]] | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.mean = cast(tuple[float, float], mean)\n        self.std = cast(tuple[float, float], std)\n        self.gauss_sigma = cast(tuple[float, float], gauss_sigma)\n        self.cutout_threshold = cast(tuple[float, float], cutout_threshold)\n        self.intensity = cast(tuple[float, float], intensity)\n        self.mode = mode\n        self.color = cast(dict[str, Sequence[int]], color)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        non_mud: np.ndarray,\n        mud: np.ndarray,\n        drops: np.ndarray,\n        mode: SpatterMode,\n        **params: dict[str, Any],\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.spatter(img, non_mud, mud, drops, mode)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        mean = self.py_random.uniform(*self.mean)\n        std = self.py_random.uniform(*self.std)\n        cutout_threshold = self.py_random.uniform(*self.cutout_threshold)\n        sigma = self.py_random.uniform(*self.gauss_sigma)\n        mode = self.py_random.choice(self.mode)\n        intensity = self.py_random.uniform(*self.intensity)\n        color = np.array(self.color[mode]) / 255.0\n\n        liquid_layer = self.random_generator.normal(\n            size=(height, width),\n            loc=mean,\n            scale=std,\n        )\n        liquid_layer = gaussian_filter(liquid_layer, sigma=sigma, mode=\"nearest\")\n        liquid_layer[liquid_layer < cutout_threshold] = 0\n\n        if mode == \"rain\":\n            liquid_layer = clip(liquid_layer * 255, np.uint8, inplace=False)\n            dist = 255 - cv2.Canny(liquid_layer, 50, 150)\n            dist = cv2.distanceTransform(dist, cv2.DIST_L2, 5)\n            _, dist = cv2.threshold(dist, 20, 20, cv2.THRESH_TRUNC)\n            dist = clip(fblur.blur(dist, 3), np.uint8, inplace=True)\n            dist = fmain.equalize(dist)\n\n            ker = np.array([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]])\n            dist = fmain.convolve(dist, ker)\n            dist = fblur.blur(dist, 3).astype(np.float32)\n\n            m = liquid_layer * dist\n            m *= 1 / np.max(m, axis=(0, 1))\n\n            drops = m[:, :, None] * color * intensity\n            mud = None\n            non_mud = None\n        else:\n            m = np.where(liquid_layer > cutout_threshold, 1, 0)\n            m = gaussian_filter(m.astype(np.float32), sigma=sigma, mode=\"nearest\")\n            m[m < 1.2 * cutout_threshold] = 0\n            m = m[..., np.newaxis]\n\n            mud = m * color\n            non_mud = 1 - m\n            drops = None\n\n        return {\n            \"non_mud\": non_mud,\n            \"mud\": mud,\n            \"drops\": drops,\n            \"mode\": mode,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str, str, str, str]:\n        return (\n            \"mean\",\n            \"std\",\n            \"gauss_sigma\",\n            \"intensity\",\n            \"cutout_threshold\",\n            \"mode\",\n            \"color\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Superpixels","title":"class Superpixels (p_replace=(0, 0.1), n_segments=(100, 100), max_size=128, interpolation=1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Transform images partially/completely to their superpixel representation.

Parameters:

Name Type Description p_replace tuple[float, float] | float

Defines for any segment the probability that the pixels within that segment are replaced by their average color (otherwise, the pixels are not changed).

  • A probability of 0.0 would mean, that the pixels in no segment are replaced by their average color (image is not changed at all).
  • A probability of 0.5 would mean, that around half of all segments are replaced by their average color.
  • A probability of 1.0 would mean, that all segments are replaced by their average color (resulting in a voronoi image).

Behavior based on chosen data types for this parameter: * If a float, then that float will always be used. * If tuple (a, b), then a random probability will be sampled from the interval [a, b] per image. Default: (0.1, 0.3)

n_segments tuple[int, int] | int

Rough target number of how many superpixels to generate. The algorithm may deviate from this number. Lower value will lead to coarser superpixels. Higher values are computationally more intensive and will hence lead to a slowdown. If tuple (a, b), then a value from the discrete interval [a..b] will be sampled per image. Default: (15, 120)

max_size int | None

Maximum image size at which the augmentation is performed. If the width or height of an image exceeds this value, it will be downscaled before the augmentation so that the longest side matches max_size. This is done to speed up the process. The final output image has the same size as the input image. Note that in case p_replace is below 1.0, the down-/upscaling will affect the not-replaced pixels too. Use None to apply no down-/upscaling. Default: 128

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • This transform can significantly change the visual appearance of the image.
  • The transform makes use of a superpixel algorithm, which tends to be slow. If performance is a concern, consider using max_size to limit the image size.
  • The effect of this transform can vary greatly depending on the p_replace and n_segments parameters.
  • When p_replace is high, the image can become highly abstracted, resembling a voronoi diagram.
  • The transform preserves the original image type (uint8 or float32).

Mathematical Formulation: 1. The image is segmented into approximately n_segments superpixels using the SLIC algorithm. 2. For each superpixel: - With probability p_replace, all pixels in the superpixel are replaced with their mean color. - With probability 1 - p_replace, the superpixel is left unchanged. 3. If the image was resized due to max_size, it is resized back to its original dimensions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-superpixels-with-default-parameters","title":"Apply superpixels with default parameters","text":"Python
>>> transform = A.Superpixels(p=1.0)\n>>> augmented_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-superpixels-with-custom-parameters","title":"Apply superpixels with custom parameters","text":"Python
>>> transform = A.Superpixels(\n...     p_replace=(0.5, 0.7),\n...     n_segments=(50, 100),\n...     max_size=None,\n...     interpolation=cv2.INTER_NEAREST,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Superpixels(ImageOnlyTransform):\n    \"\"\"Transform images partially/completely to their superpixel representation.\n\n    Args:\n        p_replace (tuple[float, float] | float): Defines for any segment the probability that the pixels within that\n            segment are replaced by their average color (otherwise, the pixels are not changed).\n\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n                segment are replaced by their average color (image is not\n                changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n                segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n                replaced by their average color (resulting in a voronoi\n                image).\n\n            Behavior based on chosen data types for this parameter:\n            * If a ``float``, then that ``float`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n            sampled from the interval ``[a, b]`` per image.\n            Default: (0.1, 0.3)\n\n        n_segments (tuple[int, int] | int): Rough target number of how many superpixels to generate.\n            The algorithm may deviate from this number.\n            Lower value will lead to coarser superpixels.\n            Higher values are computationally more intensive and will hence lead to a slowdown.\n            If tuple ``(a, b)``, then a value from the discrete interval ``[a..b]`` will be sampled per image.\n            Default: (15, 120)\n\n        max_size (int | None): Maximum image size at which the augmentation is performed.\n            If the width or height of an image exceeds this value, it will be\n            downscaled before the augmentation so that the longest side matches `max_size`.\n            This is done to speed up the process. The final output image has the same size as the input image.\n            Note that in case `p_replace` is below ``1.0``,\n            the down-/upscaling will affect the not-replaced pixels too.\n            Use ``None`` to apply no down-/upscaling.\n            Default: 128\n\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform can significantly change the visual appearance of the image.\n        - The transform makes use of a superpixel algorithm, which tends to be slow.\n        If performance is a concern, consider using `max_size` to limit the image size.\n        - The effect of this transform can vary greatly depending on the `p_replace` and `n_segments` parameters.\n        - When `p_replace` is high, the image can become highly abstracted, resembling a voronoi diagram.\n        - The transform preserves the original image type (uint8 or float32).\n\n    Mathematical Formulation:\n        1. The image is segmented into approximately `n_segments` superpixels using the SLIC algorithm.\n        2. For each superpixel:\n        - With probability `p_replace`, all pixels in the superpixel are replaced with their mean color.\n        - With probability `1 - p_replace`, the superpixel is left unchanged.\n        3. If the image was resized due to `max_size`, it is resized back to its original dimensions.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply superpixels with default parameters\n        >>> transform = A.Superpixels(p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n        # Apply superpixels with custom parameters\n        >>> transform = A.Superpixels(\n        ...     p_replace=(0.5, 0.7),\n        ...     n_segments=(50, 100),\n        ...     max_size=None,\n        ...     interpolation=cv2.INTER_NEAREST,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        p_replace: ZeroOneRangeType\n        n_segments: OnePlusIntRangeType\n        max_size: int | None = Field(ge=1)\n        interpolation: InterpolationType\n\n    def __init__(\n        self,\n        p_replace: ScaleFloatType = (0, 0.1),\n        n_segments: ScaleIntType = (100, 100),\n        max_size: int | None = 128,\n        interpolation: int = cv2.INTER_LINEAR,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.p_replace = cast(tuple[float, float], p_replace)\n        self.n_segments = cast(tuple[int, int], n_segments)\n        self.max_size = max_size\n        self.interpolation = interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"p_replace\", \"n_segments\", \"max_size\", \"interpolation\"\n\n    def get_params(self) -> dict[str, Any]:\n        n_segments = self.py_random.randint(*self.n_segments)\n        p = self.py_random.uniform(*self.p_replace)\n        return {\n            \"replace_samples\": self.random_generator.random(n_segments) < p,\n            \"n_segments\": n_segments,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        replace_samples: Sequence[bool],\n        n_segments: int,\n        **kwargs: Any,\n    ) -> np.ndarray:\n        return fmain.superpixels(\n            img,\n            n_segments,\n            replace_samples,\n            self.max_size,\n            self.interpolation,\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToFloat","title":"class ToFloat (max_value=None, p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert the input image to a floating-point representation.

This transform divides pixel values by max_value to get a float32 output array where all values lie in the range [0, 1.0]. It's useful for normalizing image data before feeding it into neural networks or other algorithms that expect float input.

Parameters:

Name Type Description max_value float | None

The maximum possible input value. If None, the transform will try to infer the maximum value by inspecting the data type of the input image: - uint8: 255 - uint16: 65535 - uint32: 4294967295 - float32: 1.0 Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: uint8, uint16, uint32, float32

Returns:

Type Description np.ndarray

Image in floating point representation, with values in range [0, 1.0].

Note

  • If the input image is already float32 with values in [0, 1], it will be returned unchanged.
  • For integer types (uint8, uint16, uint32), the function will scale the values to [0, 1] range.
  • The output will always be float32, regardless of the input type.
  • This transform is often used as a preprocessing step before applying other transformations or feeding the image into a neural network.

Exceptions:

Type Description TypeError

If the input image data type is not supported.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Convert uint8 image to float\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ToFloat(max_value=None)\n>>> float_image = transform(image=image)['image']\n>>> assert float_image.dtype == np.float32\n>>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n>>>\n# Convert uint16 image to float with custom max_value\n>>> image = np.random.randint(0, 4096, (100, 100, 3), dtype=np.uint16)\n>>> transform = A.ToFloat(max_value=4095)\n>>> float_image = transform(image=image)['image']\n>>> assert float_image.dtype == np.float32\n>>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n

See Also: FromFloat: The inverse operation, converting from float back to the original data type.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToFloat(ImageOnlyTransform):\n    \"\"\"Convert the input image to a floating-point representation.\n\n    This transform divides pixel values by `max_value` to get a float32 output array\n    where all values lie in the range [0, 1.0]. It's useful for normalizing image data\n    before feeding it into neural networks or other algorithms that expect float input.\n\n    Args:\n        max_value (float | None): The maximum possible input value. If None, the transform\n            will try to infer the maximum value by inspecting the data type of the input image:\n            - uint8: 255\n            - uint16: 65535\n            - uint32: 4294967295\n            - float32: 1.0\n            Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, uint16, uint32, float32\n\n    Returns:\n        np.ndarray: Image in floating point representation, with values in range [0, 1.0].\n\n    Note:\n        - If the input image is already float32 with values in [0, 1], it will be returned unchanged.\n        - For integer types (uint8, uint16, uint32), the function will scale the values to [0, 1] range.\n        - The output will always be float32, regardless of the input type.\n        - This transform is often used as a preprocessing step before applying other transformations\n          or feeding the image into a neural network.\n\n    Raises:\n        TypeError: If the input image data type is not supported.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Convert uint8 image to float\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ToFloat(max_value=None)\n        >>> float_image = transform(image=image)['image']\n        >>> assert float_image.dtype == np.float32\n        >>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n        >>>\n        # Convert uint16 image to float with custom max_value\n        >>> image = np.random.randint(0, 4096, (100, 100, 3), dtype=np.uint16)\n        >>> transform = A.ToFloat(max_value=4095)\n        >>> float_image = transform(image=image)['image']\n        >>> assert float_image.dtype == np.float32\n        >>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n\n    See Also:\n        FromFloat: The inverse operation, converting from float back to the original data type.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        max_value: float | None\n\n    def __init__(\n        self,\n        max_value: float | None = None,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply)\n        self.max_value = max_value\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return to_float(img, self.max_value)\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"max_value\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToGray","title":"class ToGray (num_output_channels=3, method='weighted_average', always_apply=None, p=0.5) [view source on GitHub]","text":"

Convert an image to grayscale and optionally replicate the grayscale channel.

This transform first converts a color image to a single-channel grayscale image using various methods, then replicates the grayscale channel if num_output_channels is greater than 1.

Parameters:

Name Type Description num_output_channels int

The number of channels in the output image. If greater than 1, the grayscale channel will be replicated. Default: 3.

method Literal[\"weighted_average\", \"from_lab\", \"desaturation\", \"average\", \"max\", \"pca\"]

The method used for grayscale conversion: - \"weighted_average\": Uses a weighted sum of RGB channels (0.299R + 0.587G + 0.114B). Works only with 3-channel images. Provides realistic results based on human perception. - \"from_lab\": Extracts the L channel from the LAB color space. Works only with 3-channel images. Gives perceptually uniform results. - \"desaturation\": Averages the maximum and minimum values across channels. Works with any number of channels. Fast but may not preserve perceived brightness well. - \"average\": Simple average of all channels. Works with any number of channels. Fast but may not give realistic results. - \"max\": Takes the maximum value across all channels. Works with any number of channels. Tends to produce brighter results. - \"pca\": Applies Principal Component Analysis to reduce channels. Works with any number of channels. Can preserve more information but is computationally intensive.

p float

Probability of applying the transform. Default: 0.5.

Exceptions:

Type Description TypeError

If the input image doesn't have 3 channels for methods that require it.

Note

  • The transform first converts the input image to single-channel grayscale, then replicates this channel if num_output_channels > 1.
  • \"weighted_average\" and \"from_lab\" are typically used in image processing and computer vision applications where accurate representation of human perception is important.
  • \"desaturation\" and \"average\" are often used in simple image manipulation tools or when computational speed is a priority.
  • \"max\" method can be useful in scenarios where preserving bright features is important, such as in some medical imaging applications.
  • \"pca\" might be used in advanced image analysis tasks or when dealing with hyperspectral images.

Image types: uint8, float32

Returns:

Type Description np.ndarray

Grayscale image with the specified number of channels.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToGray(ImageOnlyTransform):\n    \"\"\"Convert an image to grayscale and optionally replicate the grayscale channel.\n\n    This transform first converts a color image to a single-channel grayscale image using various methods,\n    then replicates the grayscale channel if num_output_channels is greater than 1.\n\n    Args:\n        num_output_channels (int): The number of channels in the output image. If greater than 1,\n            the grayscale channel will be replicated. Default: 3.\n        method (Literal[\"weighted_average\", \"from_lab\", \"desaturation\", \"average\", \"max\", \"pca\"]):\n            The method used for grayscale conversion:\n            - \"weighted_average\": Uses a weighted sum of RGB channels (0.299R + 0.587G + 0.114B).\n              Works only with 3-channel images. Provides realistic results based on human perception.\n            - \"from_lab\": Extracts the L channel from the LAB color space.\n              Works only with 3-channel images. Gives perceptually uniform results.\n            - \"desaturation\": Averages the maximum and minimum values across channels.\n              Works with any number of channels. Fast but may not preserve perceived brightness well.\n            - \"average\": Simple average of all channels.\n              Works with any number of channels. Fast but may not give realistic results.\n            - \"max\": Takes the maximum value across all channels.\n              Works with any number of channels. Tends to produce brighter results.\n            - \"pca\": Applies Principal Component Analysis to reduce channels.\n              Works with any number of channels. Can preserve more information but is computationally intensive.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Raises:\n        TypeError: If the input image doesn't have 3 channels for methods that require it.\n\n    Note:\n        - The transform first converts the input image to single-channel grayscale, then replicates\n          this channel if num_output_channels > 1.\n        - \"weighted_average\" and \"from_lab\" are typically used in image processing and computer vision\n          applications where accurate representation of human perception is important.\n        - \"desaturation\" and \"average\" are often used in simple image manipulation tools or when\n          computational speed is a priority.\n        - \"max\" method can be useful in scenarios where preserving bright features is important,\n          such as in some medical imaging applications.\n        - \"pca\" might be used in advanced image analysis tasks or when dealing with hyperspectral images.\n\n    Image types:\n        uint8, float32\n\n    Returns:\n        np.ndarray: Grayscale image with the specified number of channels.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_output_channels: int = Field(\n            default=3,\n            description=\"The number of output channels.\",\n            ge=1,\n        )\n        method: Literal[\n            \"weighted_average\",\n            \"from_lab\",\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        ]\n\n    def __init__(\n        self,\n        num_output_channels: int = 3,\n        method: Literal[\n            \"weighted_average\",\n            \"from_lab\",\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        ] = \"weighted_average\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_output_channels = num_output_channels\n        self.method = method\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_grayscale_image(img):\n            warnings.warn(\"The image is already gray.\", stacklevel=2)\n            return img\n\n        num_channels = get_num_channels(img)\n\n        if num_channels != NUM_RGB_CHANNELS and self.method not in {\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        }:\n            msg = \"ToGray transformation expects 3-channel images.\"\n            raise TypeError(msg)\n\n        return fmain.to_gray(img, self.num_output_channels, self.method)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_output_channels\", \"method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToRGB","title":"class ToRGB (num_output_channels=3, p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert an input image from grayscale to RGB format.

Parameters:

Name Type Description num_output_channels int

The number of channels in the output image. Default: 3.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1

Note

  • For single-channel (grayscale) images, the channel is replicated to create an RGB image.
  • If the input is already a 3-channel RGB image, it is returned unchanged.
  • This transform does not change the data type of the image (e.g., uint8 remains uint8).

Exceptions:

Type Description TypeError

If the input image has more than 1 channel.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n>>> # Convert a grayscale image to RGB\n>>> transform = A.Compose([A.ToRGB(p=1.0)])\n>>> grayscale_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n>>> rgb_image = transform(image=grayscale_image)['image']\n>>> assert rgb_image.shape == (100, 100, 3)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToRGB(ImageOnlyTransform):\n    \"\"\"Convert an input image from grayscale to RGB format.\n\n    Args:\n        num_output_channels (int): The number of channels in the output image. Default: 3.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1\n\n    Note:\n        - For single-channel (grayscale) images, the channel is replicated to create an RGB image.\n        - If the input is already a 3-channel RGB image, it is returned unchanged.\n        - This transform does not change the data type of the image (e.g., uint8 remains uint8).\n\n    Raises:\n        TypeError: If the input image has more than 1 channel.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        >>> # Convert a grayscale image to RGB\n        >>> transform = A.Compose([A.ToRGB(p=1.0)])\n        >>> grayscale_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n        >>> rgb_image = transform(image=grayscale_image)['image']\n        >>> assert rgb_image.shape == (100, 100, 3)\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_output_channels: int = Field(ge=1)\n\n    def __init__(\n        self,\n        num_output_channels: int = 3,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.num_output_channels = num_output_channels\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_rgb_image(img):\n            warnings.warn(\"The image is already an RGB.\", stacklevel=2)\n            return np.ascontiguousarray(img)\n        if not is_grayscale_image(img):\n            msg = \"ToRGB transformation expects 2-dim images or 3-dim with the last dimension equal to 1.\"\n            raise TypeError(msg)\n\n        return fmain.grayscale_to_multichannel(\n            img,\n            num_output_channels=self.num_output_channels,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"num_output_channels\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToSepia","title":"class ToSepia (p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply a sepia filter to the input image.

This transform converts a color image to a sepia tone, giving it a warm, brownish tint that is reminiscent of old photographs. The sepia effect is achieved by applying a specific color transformation matrix to the RGB channels of the input image. For grayscale images, the transform is a no-op and returns the original image.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1,3

Note

  • The sepia effect only works with RGB images (3 channels). For grayscale images, the original image is returned unchanged since the sepia transformation would have no visible effect when R=G=B.
  • The sepia effect is created using a fixed color transformation matrix: [[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]]
  • The output image will have the same data type as the input image.
  • For float32 images, ensure the input values are in the range [0, 1].

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Apply sepia effect to a uint8 RGB image\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ToSepia(p=1.0)\n>>> sepia_image = transform(image=image)['image']\n>>> assert sepia_image.shape == image.shape\n>>> assert sepia_image.dtype == np.uint8\n>>>\n# Apply sepia effect to a float32 RGB image\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)\n>>> transform = A.ToSepia(p=1.0)\n>>> sepia_image = transform(image=image)['image']\n>>> assert sepia_image.shape == image.shape\n>>> assert sepia_image.dtype == np.float32\n>>> assert 0 <= sepia_image.min() <= sepia_image.max() <= 1.0\n>>>\n# No effect on grayscale images\n>>> gray_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n>>> transform = A.ToSepia(p=1.0)\n>>> result = transform(image=gray_image)['image']\n>>> assert np.array_equal(result, gray_image)\n

Mathematical Formulation: Given an input pixel [R, G, B], the sepia tone is calculated as: R_sepia = 0.393R + 0.769G + 0.189B G_sepia = 0.349R + 0.686G + 0.168B B_sepia = 0.272R + 0.534G + 0.131*B

For grayscale images where R=G=B, this transformation would result in a simple\nscaling of the original value, so we skip it.\n\nThe output values are clipped to the valid range for the image's data type.\n

See Also: ToGray: For converting images to grayscale instead of sepia.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToSepia(ImageOnlyTransform):\n    \"\"\"Apply a sepia filter to the input image.\n\n    This transform converts a color image to a sepia tone, giving it a warm, brownish tint\n    that is reminiscent of old photographs. The sepia effect is achieved by applying a\n    specific color transformation matrix to the RGB channels of the input image.\n    For grayscale images, the transform is a no-op and returns the original image.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1,3\n\n    Note:\n        - The sepia effect only works with RGB images (3 channels). For grayscale images,\n          the original image is returned unchanged since the sepia transformation would\n          have no visible effect when R=G=B.\n        - The sepia effect is created using a fixed color transformation matrix:\n          [[0.393, 0.769, 0.189],\n           [0.349, 0.686, 0.168],\n           [0.272, 0.534, 0.131]]\n        - The output image will have the same data type as the input image.\n        - For float32 images, ensure the input values are in the range [0, 1].\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Apply sepia effect to a uint8 RGB image\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> sepia_image = transform(image=image)['image']\n        >>> assert sepia_image.shape == image.shape\n        >>> assert sepia_image.dtype == np.uint8\n        >>>\n        # Apply sepia effect to a float32 RGB image\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> sepia_image = transform(image=image)['image']\n        >>> assert sepia_image.shape == image.shape\n        >>> assert sepia_image.dtype == np.float32\n        >>> assert 0 <= sepia_image.min() <= sepia_image.max() <= 1.0\n        >>>\n        # No effect on grayscale images\n        >>> gray_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> result = transform(image=gray_image)['image']\n        >>> assert np.array_equal(result, gray_image)\n\n    Mathematical Formulation:\n        Given an input pixel [R, G, B], the sepia tone is calculated as:\n        R_sepia = 0.393*R + 0.769*G + 0.189*B\n        G_sepia = 0.349*R + 0.686*G + 0.168*B\n        B_sepia = 0.272*R + 0.534*G + 0.131*B\n\n        For grayscale images where R=G=B, this transformation would result in a simple\n        scaling of the original value, so we skip it.\n\n        The output values are clipped to the valid range for the image's data type.\n\n    See Also:\n        ToGray: For converting images to grayscale instead of sepia.\n    \"\"\"\n\n    def __init__(self, p: float = 0.5, always_apply: bool | None = None):\n        super().__init__(p, always_apply)\n        self.sepia_transformation_matrix = np.array(\n            [[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]],\n        )\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_grayscale_image(img):\n            return img\n\n        if not is_rgb_image(img):\n            msg = \"ToSepia transformation expects 1 or 3-channel images.\"\n            raise TypeError(msg)\n        return fmain.linear_transformation_rgb(img, self.sepia_transformation_matrix)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.UniformParams","title":"class UniformParams [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class UniformParams(NoiseParamsBase):\n    noise_type: Literal[\"uniform\"] = \"uniform\"\n    ranges: list[Sequence[float]] = Field(\n        description=\"List of (min, max) ranges for each channel\",\n        min_length=1,\n    )\n\n    @field_validator(\"ranges\", mode=\"after\")\n    @classmethod\n    def validate_ranges(cls, v: list[Sequence[float]]) -> list[tuple[float, float]]:\n        result = []\n        for range_values in v:\n            if len(range_values) != PAIR:\n                raise ValueError(\"Each range must have exactly 2 values\")\n            min_val, max_val = range_values\n            if not (-1 <= min_val <= max_val <= 1):\n                raise ValueError(\"Range values must be in [-1, 1] and min <= max\")\n            result.append((float(min_val), float(max_val)))\n        return result\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.UnsharpMask","title":"class UnsharpMask (blur_limit=(3, 7), sigma_limit=0.0, alpha=(0.2, 0.5), threshold=10, p=0.5, always_apply=None) [view source on GitHub]","text":"

Sharpen the input image using Unsharp Masking processing and overlays the result with the original image.

Unsharp masking is a technique that enhances edge contrast in an image, creating the illusion of increased sharpness. This transform applies Gaussian blur to create a blurred version of the image, then uses this to create a mask which is combined with the original image to enhance edges and fine details.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

maximum Gaussian kernel size for blurring the input image. Must be zero or odd and in range [0, inf). If set to 0 it will be computed from sigma as round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1. If set single value blur_limit will be in range (0, blur_limit). Default: (3, 7).

sigma_limit tuple[float, float] | float

Gaussian kernel standard deviation. Must be in range [0, inf). If set single value sigma_limit will be in range (0, sigma_limit). If set to 0 sigma will be computed as sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8. Default: 0.

alpha tuple[float, float]

range to choose the visibility of the sharpened image. At 0, only the original image is visible, at 1.0 only its sharpened version is visible. Default: (0.2, 0.5).

threshold int

Value to limit sharpening only for areas with high pixel difference between original image and it's smoothed version. Higher threshold means less sharpening on flat areas. Must be in range [0, 255]. Default: 10.

p float

probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Note

  • The algorithm creates a mask M = (I - G) * alpha, where I is the original image and G is the Gaussian blurred version.
  • The final image is computed as: output = I + M if |I - G| > threshold, else I.
  • Higher alpha values increase the strength of the sharpening effect.
  • Higher threshold values limit the sharpening effect to areas with more significant edges or details.
  • The blur_limit and sigma_limit parameters control the Gaussian blur used to create the mask.

References

  • https://en.wikipedia.org/wiki/Unsharp_masking
  • https://arxiv.org/pdf/2107.10833.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>>\n# Apply UnsharpMask with default parameters\n>>> transform = A.UnsharpMask(p=1.0)\n>>> sharpened_image = transform(image=image)['image']\n>>>\n# Apply UnsharpMask with custom parameters\n>>> transform = A.UnsharpMask(\n...     blur_limit=(3, 7),\n...     sigma_limit=(0.1, 0.5),\n...     alpha=(0.2, 0.7),\n...     threshold=15,\n...     p=1.0\n... )\n>>> sharpened_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class UnsharpMask(ImageOnlyTransform):\n    \"\"\"Sharpen the input image using Unsharp Masking processing and overlays the result with the original image.\n\n    Unsharp masking is a technique that enhances edge contrast in an image, creating the illusion of increased\n        sharpness.\n    This transform applies Gaussian blur to create a blurred version of the image, then uses this to create a mask\n    which is combined with the original image to enhance edges and fine details.\n\n    Args:\n        blur_limit (tuple[int, int] | int): maximum Gaussian kernel size for blurring the input image.\n            Must be zero or odd and in range [0, inf). If set to 0 it will be computed from sigma\n            as `round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1`.\n            If set single value `blur_limit` will be in range (0, blur_limit).\n            Default: (3, 7).\n        sigma_limit (tuple[float, float] | float): Gaussian kernel standard deviation. Must be in range [0, inf).\n            If set single value `sigma_limit` will be in range (0, sigma_limit).\n            If set to 0 sigma will be computed as `sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`. Default: 0.\n        alpha (tuple[float, float]): range to choose the visibility of the sharpened image.\n            At 0, only the original image is visible, at 1.0 only its sharpened version is visible.\n            Default: (0.2, 0.5).\n        threshold (int): Value to limit sharpening only for areas with high pixel difference between original image\n            and it's smoothed version. Higher threshold means less sharpening on flat areas.\n            Must be in range [0, 255]. Default: 10.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The algorithm creates a mask M = (I - G) * alpha, where I is the original image and G is the Gaussian\n            blurred version.\n        - The final image is computed as: output = I + M if |I - G| > threshold, else I.\n        - Higher alpha values increase the strength of the sharpening effect.\n        - Higher threshold values limit the sharpening effect to areas with more significant edges or details.\n        - The blur_limit and sigma_limit parameters control the Gaussian blur used to create the mask.\n\n    References:\n        - https://en.wikipedia.org/wiki/Unsharp_masking\n        - https://arxiv.org/pdf/2107.10833.pdf\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>>\n        # Apply UnsharpMask with default parameters\n        >>> transform = A.UnsharpMask(p=1.0)\n        >>> sharpened_image = transform(image=image)['image']\n        >>>\n        # Apply UnsharpMask with custom parameters\n        >>> transform = A.UnsharpMask(\n        ...     blur_limit=(3, 7),\n        ...     sigma_limit=(0.1, 0.5),\n        ...     alpha=(0.2, 0.7),\n        ...     threshold=15,\n        ...     p=1.0\n        ... )\n        >>> sharpened_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        sigma_limit: NonNegativeFloatRangeType\n        alpha: ZeroOneRangeType\n        threshold: int = Field(ge=0, le=255)\n        blur_limit: ScaleIntType\n\n        @field_validator(\"blur_limit\")\n        @classmethod\n        def process_blur(\n            cls,\n            value: ScaleIntType,\n            info: ValidationInfo,\n        ) -> tuple[int, int]:\n            return fblur.process_blur_limit(value, info, min_value=3)\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_limit: ScaleFloatType = 0.0,\n        alpha: ScaleFloatType = (0.2, 0.5),\n        threshold: int = 10,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_limit = cast(tuple[float, float], sigma_limit)\n        self.alpha = cast(tuple[float, float], alpha)\n        self.threshold = threshold\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"ksize\": self.py_random.randrange(\n                self.blur_limit[0],\n                self.blur_limit[1] + 1,\n                2,\n            ),\n            \"sigma\": self.py_random.uniform(*self.sigma_limit),\n            \"alpha\": self.py_random.uniform(*self.alpha),\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        ksize: int,\n        sigma: int,\n        alpha: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.unsharp_mask(\n            img,\n            ksize,\n            sigma=sigma,\n            alpha=alpha,\n            threshold=self.threshold,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"blur_limit\", \"sigma_limit\", \"alpha\", \"threshold\"\n
"},{"location":"api_reference/augmentations/blur/","title":"Index","text":"
  • Blur transforms (albumentations.augmentations.blur.transforms)
"},{"location":"api_reference/augmentations/blur/functional/","title":"Blur functional transforms (augmentations.blur.functional)","text":""},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.create_motion_kernel","title":"def create_motion_kernel (kernel_size, angle, direction, allow_shifted, random_state) [view source on GitHub]","text":"

Create a motion blur kernel.

Parameters:

Name Type Description kernel_size int

Size of the kernel (must be odd)

angle float

Angle in degrees (counter-clockwise)

direction float

Blur direction (-1.0 to 1.0)

allow_shifted bool

Allow kernel to be randomly shifted from center

random_state Random

Python's random.Random instance

Returns:

Type Description np.ndarray

Motion blur kernel

Source code in albumentations/augmentations/blur/functional.py Python
def create_motion_kernel(\n    kernel_size: int,\n    angle: float,\n    direction: float,\n    allow_shifted: bool,\n    random_state: Random,\n) -> np.ndarray:\n    \"\"\"Create a motion blur kernel.\n\n    Args:\n        kernel_size: Size of the kernel (must be odd)\n        angle: Angle in degrees (counter-clockwise)\n        direction: Blur direction (-1.0 to 1.0)\n        allow_shifted: Allow kernel to be randomly shifted from center\n        random_state: Python's random.Random instance\n\n    Returns:\n        Motion blur kernel\n    \"\"\"\n    kernel = np.zeros((kernel_size, kernel_size), dtype=np.float32)\n    center = kernel_size // 2\n\n    # Convert angle to radians\n    angle_rad = np.deg2rad(angle)\n\n    # Calculate direction vector\n    dx = np.cos(angle_rad)\n    dy = np.sin(angle_rad)\n\n    # Create line points with direction bias\n    line_length = kernel_size // 2\n    t = np.linspace(-line_length, line_length, kernel_size * 2)\n\n    # Apply direction bias\n    if direction != 0:\n        t = t * (1 + abs(direction))\n        if direction < 0:\n            t = t * -1\n\n    # Generate line coordinates\n    x = center + dx * t\n    y = center + dy * t\n\n    # Apply random shift if allowed\n    if allow_shifted and random_state is not None:\n        shift_x = random_state.uniform(-1, 1) * line_length / 2\n        shift_y = random_state.uniform(-1, 1) * line_length / 2\n        x += shift_x\n        y += shift_y\n\n    # Round coordinates and clip to kernel bounds\n    x = np.clip(np.round(x), 0, kernel_size - 1).astype(int)\n    y = np.clip(np.round(y), 0, kernel_size - 1).astype(int)\n\n    # Keep only unique points to avoid multiple assignments\n    points = np.unique(np.column_stack([y, x]), axis=0)\n    kernel[points[:, 0], points[:, 1]] = 1\n\n    # Ensure at least one point is set\n    if not kernel.any():\n        kernel[center, center] = 1\n\n    return kernel\n
"},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.process_blur_limit","title":"def process_blur_limit (value, info, min_value=0) [view source on GitHub]","text":"

Process blur limit to ensure valid kernel sizes.

Source code in albumentations/augmentations/blur/functional.py Python
def process_blur_limit(value: ScaleIntType, info: ValidationInfo, min_value: int = 0) -> tuple[int, int]:\n    \"\"\"Process blur limit to ensure valid kernel sizes.\"\"\"\n    result = value if isinstance(value, Sequence) else (min_value, value)\n\n    result = _ensure_min_value(result, min_value, info.field_name)\n    result = _ensure_odd_values(result, info.field_name)\n\n    if result[0] > result[1]:\n        final_result = (result[1], result[1])\n        warn(\n            f\"{info.field_name}: Invalid range {result} (min > max). \"\n            f\"Range automatically adjusted to {final_result}.\",\n            UserWarning,\n            stacklevel=2,\n        )\n        return final_result\n\n    return result\n
"},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.sample_odd_from_range","title":"def sample_odd_from_range (random_state, low, high) [view source on GitHub]","text":"

Sample an odd number from the range [low, high] (inclusive).

Parameters:

Name Type Description random_state Random

instance of random.Random

low int

lower bound (will be converted to nearest valid odd number)

high int

upper bound (will be converted to nearest valid odd number)

Returns:

Type Description int

Randomly sampled odd number from the range

Note

  • Input values will be converted to nearest valid odd numbers:
  • Values less than 3 will become 3
  • Even values will be rounded up to next odd number
  • After normalization, high must be >= low
Source code in albumentations/augmentations/blur/functional.py Python
def sample_odd_from_range(random_state: Random, low: int, high: int) -> int:\n    \"\"\"Sample an odd number from the range [low, high] (inclusive).\n\n    Args:\n        random_state: instance of random.Random\n        low: lower bound (will be converted to nearest valid odd number)\n        high: upper bound (will be converted to nearest valid odd number)\n\n    Returns:\n        Randomly sampled odd number from the range\n\n    Note:\n        - Input values will be converted to nearest valid odd numbers:\n          * Values less than 3 will become 3\n          * Even values will be rounded up to next odd number\n        - After normalization, high must be >= low\n    \"\"\"\n    # Normalize low value\n    low = max(3, low + (low % 2 == 0))\n    # Normalize high value\n    high = max(3, high + (high % 2 == 0))\n\n    # Ensure high >= low after normalization\n    high = max(high, low)\n\n    if low == high:\n        return low\n\n    # Calculate number of possible odd values\n    num_odd_values = (high - low) // 2 + 1\n    # Generate random index and convert to corresponding odd number\n    rand_idx = random_state.randint(0, num_odd_values - 1)\n    return low + (2 * rand_idx)\n
"},{"location":"api_reference/augmentations/blur/transforms/","title":"Blur transforms (augmentations.blur.transforms)","text":""},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.AdvancedBlur","title":"class AdvancedBlur (blur_limit=(3, 7), sigma_x_limit=(0.2, 1.0), sigma_y_limit=(0.2, 1.0), sigmaX_limit=None, sigmaY_limit=None, rotate_limit=(-90, 90), beta_limit=(0.5, 8.0), noise_limit=(0.9, 1.1), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies a Generalized Gaussian blur to the input image with randomized parameters for advanced data augmentation.

This transform creates a custom blur kernel based on the Generalized Gaussian distribution, which allows for a wide range of blur effects beyond standard Gaussian blur. It then applies this kernel to the input image through convolution. The transform also incorporates noise into the kernel, resulting in a unique combination of blurring and noise injection.

Key features of this augmentation:

  1. Generalized Gaussian Kernel: Uses a generalized normal distribution to create kernels that can range from box-like blurs to very peaked blurs, controlled by the beta parameter.

  2. Anisotropic Blurring: Allows for different blur strengths in horizontal and vertical directions (controlled by sigma_x and sigma_y), and rotation of the kernel.

  3. Kernel Noise: Adds multiplicative noise to the kernel before applying it to the image, creating more diverse and realistic blur effects.

Implementation Details: The kernel is generated using a 2D Generalized Gaussian function. The process involves: 1. Creating a 2D grid based on the kernel size 2. Applying rotation to this grid 3. Calculating the kernel values using the Generalized Gaussian formula 4. Adding multiplicative noise to the kernel 5. Normalizing the kernel

The resulting kernel is then applied to the image using convolution.\n

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the size of the blur kernel. If a single int is provided, the kernel size will be randomly chosen between 3 and that value. Must be odd and \u2265 3. Larger values create stronger blur effects. Default: (3, 7)

sigma_x_limit tuple[float, float] | float

Controls the spread of the blur in the x direction. Higher values increase blur strength. If a single float is provided, the range will be (0, limit). Default: (0.2, 1.0)

sigma_y_limit tuple[float, float] | float

Controls the spread of the blur in the y direction. Higher values increase blur strength. If a single float is provided, the range will be (0, limit). Default: (0.2, 1.0)

rotate_limit tuple[int, int] | int

Range of angles (in degrees) for rotating the kernel. This rotation allows for diagonal blur directions. If limit is a single int, an angle is picked from (-rotate_limit, rotate_limit). Default: (-90, 90)

beta_limit tuple[float, float] | float

Shape parameter of the Generalized Gaussian distribution. - beta = 1 gives a standard Gaussian distribution - beta < 1 creates heavier tails, resulting in more uniform, box-like blur - beta > 1 creates lighter tails, resulting in more peaked, focused blur Default: (0.5, 8.0)

noise_limit tuple[float, float] | float

Controls the strength of multiplicative noise applied to the kernel. Values around 1.0 keep the original kernel mostly intact, while values further from 1.0 introduce more variation. Default: (0.75, 1.25)

p float

Probability of applying the transform. Default: 0.5

Notes

  • This transform is particularly useful for simulating complex, real-world blur effects that go beyond simple Gaussian blur.
  • The combination of blur and noise can help in creating more robust models by simulating a wider range of image degradations.
  • Extreme values, especially for beta and noise, may result in unrealistic effects and should be used cautiously.

Reference

This transform is inspired by techniques described in: \"Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data\" https://arxiv.org/abs/2107.10833

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class AdvancedBlur(ImageOnlyTransform):\n    \"\"\"Applies a Generalized Gaussian blur to the input image with randomized parameters for advanced data augmentation.\n\n    This transform creates a custom blur kernel based on the Generalized Gaussian distribution,\n    which allows for a wide range of blur effects beyond standard Gaussian blur. It then applies\n    this kernel to the input image through convolution. The transform also incorporates noise\n    into the kernel, resulting in a unique combination of blurring and noise injection.\n\n    Key features of this augmentation:\n\n    1. Generalized Gaussian Kernel: Uses a generalized normal distribution to create kernels\n       that can range from box-like blurs to very peaked blurs, controlled by the beta parameter.\n\n    2. Anisotropic Blurring: Allows for different blur strengths in horizontal and vertical\n       directions (controlled by sigma_x and sigma_y), and rotation of the kernel.\n\n    3. Kernel Noise: Adds multiplicative noise to the kernel before applying it to the image,\n       creating more diverse and realistic blur effects.\n\n    Implementation Details:\n        The kernel is generated using a 2D Generalized Gaussian function. The process involves:\n        1. Creating a 2D grid based on the kernel size\n        2. Applying rotation to this grid\n        3. Calculating the kernel values using the Generalized Gaussian formula\n        4. Adding multiplicative noise to the kernel\n        5. Normalizing the kernel\n\n        The resulting kernel is then applied to the image using convolution.\n\n    Args:\n        blur_limit (tuple[int, int] | int, optional): Controls the size of the blur kernel. If a single int\n            is provided, the kernel size will be randomly chosen between 3 and that value.\n            Must be odd and \u2265 3. Larger values create stronger blur effects.\n            Default: (3, 7)\n\n        sigma_x_limit (tuple[float, float] | float): Controls the spread of the blur in the x direction.\n            Higher values increase blur strength.\n            If a single float is provided, the range will be (0, limit).\n            Default: (0.2, 1.0)\n\n        sigma_y_limit (tuple[float, float] | float): Controls the spread of the blur in the y direction.\n            Higher values increase blur strength.\n            If a single float is provided, the range will be (0, limit).\n            Default: (0.2, 1.0)\n\n        rotate_limit (tuple[int, int] | int): Range of angles (in degrees) for rotating the kernel.\n            This rotation allows for diagonal blur directions. If limit is a single int, an angle is picked\n            from (-rotate_limit, rotate_limit).\n            Default: (-90, 90)\n\n        beta_limit (tuple[float, float] | float): Shape parameter of the Generalized Gaussian distribution.\n            - beta = 1 gives a standard Gaussian distribution\n            - beta < 1 creates heavier tails, resulting in more uniform, box-like blur\n            - beta > 1 creates lighter tails, resulting in more peaked, focused blur\n            Default: (0.5, 8.0)\n\n        noise_limit (tuple[float, float] | float): Controls the strength of multiplicative noise\n            applied to the kernel. Values around 1.0 keep the original kernel mostly intact,\n            while values further from 1.0 introduce more variation.\n            Default: (0.75, 1.25)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - This transform is particularly useful for simulating complex, real-world blur effects\n          that go beyond simple Gaussian blur.\n        - The combination of blur and noise can help in creating more robust models by simulating\n          a wider range of image degradations.\n        - Extreme values, especially for beta and noise, may result in unrealistic effects and\n          should be used cautiously.\n\n    Reference:\n        This transform is inspired by techniques described in:\n        \"Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data\"\n        https://arxiv.org/abs/2107.10833\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        sigma_x_limit: NonNegativeFloatRangeType\n        sigma_y_limit: NonNegativeFloatRangeType\n        beta_limit: NonNegativeFloatRangeType\n        noise_limit: NonNegativeFloatRangeType\n        rotate_limit: SymmetricRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_beta_limit(cls, value: ScaleFloatType) -> tuple[float, float]:\n            result = to_tuple(value, low=0)\n            if not (result[0] < 1.0 < result[1]):\n                msg = \"beta_limit is expected to include 1.0.\"\n                raise ValueError(msg)\n            return result\n\n        @model_validator(mode=\"after\")\n        def validate_limits(self) -> Self:\n            if (\n                isinstance(self.sigma_x_limit, (tuple, list))\n                and self.sigma_x_limit[0] == 0\n                and isinstance(self.sigma_y_limit, (tuple, list))\n                and self.sigma_y_limit[0] == 0\n            ):\n                msg = \"sigma_x_limit and sigma_y_limit minimum value cannot be both equal to 0.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_x_limit: ScaleFloatType = (0.2, 1.0),\n        sigma_y_limit: ScaleFloatType = (0.2, 1.0),\n        sigmaX_limit: ScaleFloatType | None = None,  # noqa: N803\n        sigmaY_limit: ScaleFloatType | None = None,  # noqa: N803\n        rotate_limit: ScaleIntType = (-90, 90),\n        beta_limit: ScaleFloatType = (0.5, 8.0),\n        noise_limit: ScaleFloatType = (0.9, 1.1),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        if sigmaX_limit is not None:\n            warnings.warn(\n                \"sigmaX_limit is deprecated; use sigma_x_limit instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            sigma_x_limit = sigmaX_limit\n\n        if sigmaY_limit is not None:\n            warnings.warn(\n                \"sigmaY_limit is deprecated; use sigma_y_limit instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            sigma_y_limit = sigmaY_limit\n\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_x_limit = cast(tuple[float, float], sigma_x_limit)\n        self.sigma_y_limit = cast(tuple[float, float], sigma_y_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n        self.noise_limit = cast(tuple[float, float], noise_limit)\n\n    def apply(self, img: np.ndarray, kernel: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel=kernel)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n        sigma_x = self.py_random.uniform(*self.sigma_x_limit)\n        sigma_y = self.py_random.uniform(*self.sigma_y_limit)\n        angle = np.deg2rad(self.py_random.uniform(*self.rotate_limit))\n\n        # Split into 2 cases to avoid selection of narrow kernels (beta > 1) too often.\n        beta = (\n            self.py_random.uniform(self.beta_limit[0], 1)\n            if self.py_random.random() < HALF\n            else self.py_random.uniform(1, self.beta_limit[1])\n        )\n\n        noise_matrix = self.random_generator.uniform(\n            *self.noise_limit,\n            size=(ksize, ksize),\n        )\n\n        # Generate mesh grid centered at zero.\n        ax = np.arange(-ksize // 2 + 1.0, ksize // 2 + 1.0)\n        # > Shape (ksize, ksize, 2)\n        grid = np.stack(np.meshgrid(ax, ax), axis=-1)\n\n        # Calculate rotated sigma matrix\n        d_matrix = np.array([[sigma_x**2, 0], [0, sigma_y**2]])\n        u_matrix = np.array(\n            [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]],\n        )\n        sigma_matrix = np.dot(u_matrix, np.dot(d_matrix, u_matrix.T))\n\n        inverse_sigma = np.linalg.inv(sigma_matrix)\n        # Described in \"Parameter Estimation For Multivariate Generalized Gaussian Distributions\"\n        kernel = np.exp(\n            -0.5 * np.power(np.sum(np.dot(grid, inverse_sigma) * grid, 2), beta),\n        )\n        # Add noise\n        kernel *= noise_matrix\n\n        # Normalize kernel\n        kernel = kernel.astype(np.float32) / np.sum(kernel)\n        return {\"kernel\": kernel}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str, str, str]:\n        return (\n            \"blur_limit\",\n            \"sigma_x_limit\",\n            \"sigma_y_limit\",\n            \"rotate_limit\",\n            \"beta_limit\",\n            \"noise_limit\",\n        )\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.Blur","title":"class Blur (blur_limit=(3, 7), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply uniform box blur to the input image using a randomly sized square kernel.

This transform uses OpenCV's cv2.blur function, which performs a simple box filter blur. The size of the blur kernel is randomly selected for each application, allowing for varying degrees of blur intensity.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the range of the blur kernel size. - If a single int is provided, the kernel size will be randomly chosen between 3 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. The kernel size must be odd and greater than or equal to 3. Larger kernel sizes produce stronger blur effects. Default: (3, 7)

p float

Probability of applying the transform. Default: 0.5

Notes

  • The blur kernel is always square (same width and height).
  • Only odd kernel sizes are used to ensure the blur has a clear center pixel.
  • Box blur is faster than Gaussian blur but may produce less natural results.
  • This blur method averages all pixels under the kernel area, which can reduce noise but also reduce image detail.

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Blur(blur_limit=(3, 7), p=1.0)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class Blur(ImageOnlyTransform):\n    \"\"\"Apply uniform box blur to the input image using a randomly sized square kernel.\n\n    This transform uses OpenCV's cv2.blur function, which performs a simple box filter blur.\n    The size of the blur kernel is randomly selected for each application, allowing for\n    varying degrees of blur intensity.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Controls the range of the blur kernel size.\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 3 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            The kernel size must be odd and greater than or equal to 3.\n            Larger kernel sizes produce stronger blur effects.\n            Default: (3, 7)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - The blur kernel is always square (same width and height).\n        - Only odd kernel sizes are used to ensure the blur has a clear center pixel.\n        - Box blur is faster than Gaussian blur but may produce less natural results.\n        - This blur method averages all pixels under the kernel area, which can\n          reduce noise but also reduce image detail.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Blur(blur_limit=(3, 7), p=1.0)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        pass\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fblur.blur(img, kernel)\n\n    def get_params(self) -> dict[str, Any]:\n        kernel = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n        return {\"kernel\": kernel}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"blur_limit\",)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.BlurInitSchema","title":"class BlurInitSchema [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class BlurInitSchema(BaseTransformInitSchema):\n    blur_limit: ScaleIntType\n\n    @field_validator(\"blur_limit\")\n    @classmethod\n    def process_blur(cls, value: ScaleIntType, info: ValidationInfo) -> tuple[int, int]:\n        return fblur.process_blur_limit(value, info, min_value=3)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.Defocus","title":"class Defocus (radius=(3, 10), alias_blur=(0.1, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply defocus blur to the input image.

This transform simulates the effect of an out-of-focus camera by applying a defocus blur to the image. It uses a combination of disc kernels and Gaussian blur to create a realistic defocus effect.

Parameters:

Name Type Description radius tuple[int, int] | int

Range for the radius of the defocus blur. If a single int is provided, the range will be [1, radius]. Larger values create a stronger blur effect. Default: (3, 10)

alias_blur tuple[float, float] | float

Range for the standard deviation of the Gaussian blur applied after the main defocus blur. This helps to reduce aliasing artifacts. If a single float is provided, the range will be (0, alias_blur). Larger values create a smoother, more aliased effect. Default: (0.1, 0.5)

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • The defocus effect is created using a disc kernel, which simulates the shape of a camera's aperture.
  • The additional Gaussian blur (alias_blur) helps to soften the edges of the disc kernel, creating a more natural-looking defocus effect.
  • Larger radius values will create a stronger, more noticeable defocus effect.
  • The alias_blur parameter can be used to fine-tune the appearance of the defocus, with larger values creating a smoother, potentially more realistic effect.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Defocus(radius=(4, 8), alias_blur=(0.2, 0.4), always_apply=True)\n>>> result = transform(image=image)\n>>> defocused_image = result['image']\n

References

  • https://en.wikipedia.org/wiki/Defocus_aberration
  • https://www.researchgate.net/publication/261311609_Realistic_Defocus_Blur_for_Multiplane_Computer-Generated_Holography

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class Defocus(ImageOnlyTransform):\n    \"\"\"Apply defocus blur to the input image.\n\n    This transform simulates the effect of an out-of-focus camera by applying a defocus blur\n    to the image. It uses a combination of disc kernels and Gaussian blur to create a realistic\n    defocus effect.\n\n    Args:\n        radius (tuple[int, int] | int): Range for the radius of the defocus blur.\n            If a single int is provided, the range will be [1, radius].\n            Larger values create a stronger blur effect.\n            Default: (3, 10)\n\n        alias_blur (tuple[float, float] | float): Range for the standard deviation of the Gaussian blur\n            applied after the main defocus blur. This helps to reduce aliasing artifacts.\n            If a single float is provided, the range will be (0, alias_blur).\n            Larger values create a smoother, more aliased effect.\n            Default: (0.1, 0.5)\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The defocus effect is created using a disc kernel, which simulates the shape of a camera's aperture.\n        - The additional Gaussian blur (alias_blur) helps to soften the edges of the disc kernel, creating a\n          more natural-looking defocus effect.\n        - Larger radius values will create a stronger, more noticeable defocus effect.\n        - The alias_blur parameter can be used to fine-tune the appearance of the defocus, with larger values\n          creating a smoother, potentially more realistic effect.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Defocus(radius=(4, 8), alias_blur=(0.2, 0.4), always_apply=True)\n        >>> result = transform(image=image)\n        >>> defocused_image = result['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Defocus_aberration\n        - https://www.researchgate.net/publication/261311609_Realistic_Defocus_Blur_for_Multiplane_Computer-Generated_Holography\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        radius: OnePlusIntRangeType\n        alias_blur: NonNegativeFloatRangeType\n\n    def __init__(\n        self,\n        radius: ScaleIntType = (3, 10),\n        alias_blur: ScaleFloatType = (0.1, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.radius = cast(tuple[int, int], radius)\n        self.alias_blur = cast(tuple[float, float], alias_blur)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        radius: int,\n        alias_blur: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.defocus(img, radius, alias_blur)\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"radius\": self.py_random.randint(*self.radius),\n            \"alias_blur\": self.py_random.uniform(*self.alias_blur),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"radius\", \"alias_blur\")\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.GaussianBlur","title":"class GaussianBlur (blur_limit=(3, 7), sigma_limit=0, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Gaussian blur to the input image using a randomly sized kernel.

This transform blurs the input image using a Gaussian filter with a random kernel size and sigma value. Gaussian blur is a widely used image processing technique that reduces image noise and detail, creating a smoothing effect.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the range of the Gaussian kernel size. - If a single int is provided, the kernel size will be randomly chosen between 0 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. Must be zero or odd and in range [0, inf). If set to 0, it will be computed from sigma as round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1. Larger kernel sizes produce stronger blur effects. Default: (3, 7)

sigma_limit tuple[float, float] | float

Range for the Gaussian kernel standard deviation (sigma). Must be in range [0, inf). - If a single float is provided, sigma will be randomly chosen between 0 and that value. - If a tuple of two floats is provided, it defines the inclusive range of possible sigma values. If set to 0, sigma will be computed as sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8. Larger sigma values produce stronger blur effects. Default: 0

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The relationship between kernel size and sigma affects the blur strength: larger kernel sizes allow for stronger blurring effects.
  • When both blur_limit and sigma_limit are set to ranges starting from 0, the blur_limit minimum is automatically set to 3 to ensure a valid kernel size.
  • For uint8 images, the computation might be faster than for floating-point images.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.GaussianBlur(blur_limit=(3, 7), sigma_limit=(0.1, 2), p=1)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class GaussianBlur(ImageOnlyTransform):\n    \"\"\"Apply Gaussian blur to the input image using a randomly sized kernel.\n\n    This transform blurs the input image using a Gaussian filter with a random kernel size\n    and sigma value. Gaussian blur is a widely used image processing technique that reduces\n    image noise and detail, creating a smoothing effect.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Controls the range of the Gaussian kernel size.\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 0 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            Must be zero or odd and in range [0, inf). If set to 0, it will be computed\n            from sigma as `round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1`.\n            Larger kernel sizes produce stronger blur effects.\n            Default: (3, 7)\n\n        sigma_limit (tuple[float, float] | float): Range for the Gaussian kernel standard\n            deviation (sigma). Must be in range [0, inf).\n            - If a single float is provided, sigma will be randomly chosen\n              between 0 and that value.\n            - If a tuple of two floats is provided, it defines the inclusive range\n              of possible sigma values.\n            If set to 0, sigma will be computed as `sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`.\n            Larger sigma values produce stronger blur effects.\n            Default: 0\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The relationship between kernel size and sigma affects the blur strength:\n          larger kernel sizes allow for stronger blurring effects.\n        - When both blur_limit and sigma_limit are set to ranges starting from 0,\n          the blur_limit minimum is automatically set to 3 to ensure a valid kernel size.\n        - For uint8 images, the computation might be faster than for floating-point images.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.GaussianBlur(blur_limit=(3, 7), sigma_limit=(0.1, 2), p=1)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        sigma_limit: NonNegativeFloatRangeType\n\n        @field_validator(\"blur_limit\")\n        @classmethod\n        def process_blur(\n            cls,\n            value: ScaleIntType,\n            info: ValidationInfo,\n        ) -> tuple[int, int]:\n            return fblur.process_blur_limit(value, info, min_value=0)\n\n        @model_validator(mode=\"after\")\n        def validate_limits(self) -> Self:\n            if (\n                isinstance(self.blur_limit, (tuple, list))\n                and self.blur_limit[0] == 0\n                and isinstance(self.sigma_limit, (tuple, list))\n                and self.sigma_limit[0] == 0\n            ):\n                self.blur_limit = 3, max(3, self.blur_limit[1])\n                warnings.warn(\n                    \"blur_limit and sigma_limit minimum value can not be both equal to 0. \"\n                    \"blur_limit minimum value changed to 3.\",\n                    stacklevel=2,\n                )\n\n            return self\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_limit: ScaleFloatType = 0,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p, always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_limit = cast(tuple[float, float], sigma_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        ksize: int,\n        sigma: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.gaussian_blur(img, ksize, sigma=sigma)\n\n    def get_params(self) -> dict[str, float]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n\n        return {\"ksize\": ksize, \"sigma\": self.py_random.uniform(*self.sigma_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"blur_limit\", \"sigma_limit\"\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.GlassBlur","title":"class GlassBlur (sigma=0.7, max_delta=4, iterations=2, mode='fast', always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply a glass blur effect to the input image.

This transform simulates the effect of looking through textured glass by locally shuffling pixels in the image. It creates a distorted, frosted glass-like appearance.

Parameters:

Name Type Description sigma float

Standard deviation for the Gaussian kernel used in the process. Higher values increase the blur effect. Must be non-negative. Default: 0.7

max_delta int

Maximum distance in pixels for shuffling. Determines how far pixels can be moved. Larger values create more distortion. Must be a positive integer. Default: 4

iterations int

Number of times to apply the glass blur effect. More iterations create a stronger effect but increase computation time. Must be a positive integer. Default: 2

mode Literal[\"fast\", \"exact\"]

Mode of computation. Options are: - \"fast\": Uses a faster but potentially less accurate method. - \"exact\": Uses a slower but more precise method. Default: \"fast\"

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform is particularly effective for creating a 'looking through glass' effect or simulating the view through a frosted window.
  • The 'fast' mode is recommended for most use cases as it provides a good balance between effect quality and computation speed.
  • Increasing 'iterations' will strengthen the effect but also increase the processing time linearly.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.GlassBlur(sigma=0.7, max_delta=4, iterations=3, mode=\"fast\", p=1)\n>>> result = transform(image=image)\n>>> glass_blurred_image = result[\"image\"]\n

References

  • This implementation is based on the technique described in: \"ImageNet-trained CNNs are biased towards texture; increasing shape bias improves accuracy and robustness\" https://arxiv.org/abs/1903.12261
  • Original implementation: https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class GlassBlur(ImageOnlyTransform):\n    \"\"\"Apply a glass blur effect to the input image.\n\n    This transform simulates the effect of looking through textured glass by locally\n    shuffling pixels in the image. It creates a distorted, frosted glass-like appearance.\n\n    Args:\n        sigma (float): Standard deviation for the Gaussian kernel used in the process.\n            Higher values increase the blur effect. Must be non-negative.\n            Default: 0.7\n\n        max_delta (int): Maximum distance in pixels for shuffling.\n            Determines how far pixels can be moved. Larger values create more distortion.\n            Must be a positive integer.\n            Default: 4\n\n        iterations (int): Number of times to apply the glass blur effect.\n            More iterations create a stronger effect but increase computation time.\n            Must be a positive integer.\n            Default: 2\n\n        mode (Literal[\"fast\", \"exact\"]): Mode of computation. Options are:\n            - \"fast\": Uses a faster but potentially less accurate method.\n            - \"exact\": Uses a slower but more precise method.\n            Default: \"fast\"\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform is particularly effective for creating a 'looking through\n          glass' effect or simulating the view through a frosted window.\n        - The 'fast' mode is recommended for most use cases as it provides a good\n          balance between effect quality and computation speed.\n        - Increasing 'iterations' will strengthen the effect but also increase the\n          processing time linearly.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.GlassBlur(sigma=0.7, max_delta=4, iterations=3, mode=\"fast\", p=1)\n        >>> result = transform(image=image)\n        >>> glass_blurred_image = result[\"image\"]\n\n    References:\n        - This implementation is based on the technique described in:\n          \"ImageNet-trained CNNs are biased towards texture; increasing shape bias improves accuracy and robustness\"\n          https://arxiv.org/abs/1903.12261\n        - Original implementation:\n          https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        sigma: float = Field(ge=0)\n        max_delta: int = Field(ge=1)\n        iterations: int = Field(ge=1)\n        mode: Literal[\"fast\", \"exact\"]\n\n    def __init__(\n        self,\n        sigma: float = 0.7,\n        max_delta: int = 4,\n        iterations: int = 2,\n        mode: Literal[\"fast\", \"exact\"] = \"fast\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.sigma = sigma\n        self.max_delta = max_delta\n        self.iterations = iterations\n        self.mode = mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        *args: Any,\n        dxy: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.glass_blur(\n            img,\n            self.sigma,\n            self.max_delta,\n            self.iterations,\n            dxy,\n            self.mode,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n\n        # generate array containing all necessary values for transformations\n        width_pixels = height - self.max_delta * 2\n        height_pixels = width - self.max_delta * 2\n        total_pixels = int(width_pixels * height_pixels)\n        dxy = self.random_generator.integers(\n            -self.max_delta,\n            self.max_delta,\n            size=(total_pixels, self.iterations, 2),\n        )\n\n        return {\"dxy\": dxy}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"sigma\", \"max_delta\", \"iterations\", \"mode\"\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.MedianBlur","title":"class MedianBlur (blur_limit=7, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply median blur to the input image.

This transform uses a median filter to blur the input image. Median filtering is particularly effective at removing salt-and-pepper noise while preserving edges, making it a popular choice for noise reduction in image processing.

Parameters:

Name Type Description blur_limit int | tuple[int, int]

Maximum aperture linear size for blurring the input image. Must be odd and in the range [3, inf). - If a single int is provided, the kernel size will be randomly chosen between 3 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. Default: (3, 7)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The kernel size (aperture linear size) must always be odd and greater than 1.
  • Unlike mean blur or Gaussian blur, median blur uses the median of all pixels under the kernel area, making it more robust to outliers.
  • This transform is particularly useful for:
  • Removing salt-and-pepper noise
  • Preserving edges while smoothing images
  • Pre-processing images for edge detection algorithms
  • For color images, the median is calculated independently for each channel.
  • Larger kernel sizes result in stronger blurring effects but may also remove fine details from the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.MedianBlur(blur_limit=(3, 7), p=0.5)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

References

  • Median filter: https://en.wikipedia.org/wiki/Median_filter
  • OpenCV medianBlur: https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class MedianBlur(Blur):\n    \"\"\"Apply median blur to the input image.\n\n    This transform uses a median filter to blur the input image. Median filtering is particularly\n    effective at removing salt-and-pepper noise while preserving edges, making it a popular choice\n    for noise reduction in image processing.\n\n    Args:\n        blur_limit (int | tuple[int, int]): Maximum aperture linear size for blurring the input image.\n            Must be odd and in the range [3, inf).\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 3 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            Default: (3, 7)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The kernel size (aperture linear size) must always be odd and greater than 1.\n        - Unlike mean blur or Gaussian blur, median blur uses the median of all pixels under\n          the kernel area, making it more robust to outliers.\n        - This transform is particularly useful for:\n          * Removing salt-and-pepper noise\n          * Preserving edges while smoothing images\n          * Pre-processing images for edge detection algorithms\n        - For color images, the median is calculated independently for each channel.\n        - Larger kernel sizes result in stronger blurring effects but may also remove\n          fine details from the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.MedianBlur(blur_limit=(3, 7), p=0.5)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n\n    References:\n        - Median filter: https://en.wikipedia.org/wiki/Median_filter\n        - OpenCV medianBlur: https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9\n    \"\"\"\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = 7,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(blur_limit=blur_limit, p=p, always_apply=always_apply)\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fblur.median_blur(img, kernel)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.MotionBlur","title":"class MotionBlur (blur_limit=7, allow_shifted=True, angle_range=(0, 360), direction_range=(-1.0, 1.0), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply motion blur to the input image using a directional kernel.

This transform simulates motion blur effects that occur during image capture, such as camera shake or object movement. It creates a directional blur using a line-shaped kernel with controllable angle, direction, and position.

Parameters:

Name Type Description blur_limit int | tuple[int, int]

Maximum kernel size for blurring. Should be in range [3, inf). - If int: kernel size will be randomly chosen from [3, blur_limit] - If tuple: kernel size will be randomly chosen from [min, max] Larger values create stronger blur effects. Default: (3, 7)

angle_range tuple[float, float]

Range of possible angles in degrees. Controls the rotation of the motion blur line: - 0\u00b0: Horizontal motion blur \u2192 - 45\u00b0: Diagonal motion blur \u2197 - 90\u00b0: Vertical motion blur \u2191 - 135\u00b0: Diagonal motion blur \u2196 Default: (0, 360)

direction_range tuple[float, float]

Range for motion bias. Controls how the blur extends from the center: - -1.0: Blur extends only backward (\u2190) - 0.0: Blur extends equally in both directions (\u2190\u2192) - 1.0: Blur extends only forward (\u2192) For example, with angle=0: - direction=-1.0: \u2190\u2022 - direction=0.0: \u2190\u2022\u2192 - direction=1.0: \u2022\u2192 Default: (-1.0, 1.0)

allow_shifted bool

Allow random kernel position shifts. - If True: Kernel can be randomly offset from center - If False: Kernel will always be centered Default: True

p float

Probability of applying the transform. Default: 0.5

Examples of angle vs direction: 1. Horizontal motion (angle=0\u00b0): - direction=0.0: \u2190\u2022\u2192 (symmetric blur) - direction=1.0: \u2022\u2192 (forward blur) - direction=-1.0: \u2190\u2022 (backward blur)

2. Vertical motion (angle=90\u00b0):\n   - direction=0.0:   \u2191\u2022\u2193   (symmetric blur)\n   - direction=1.0:    \u2022\u2191   (upward blur)\n   - direction=-1.0:  \u2193\u2022    (downward blur)\n\n3. Diagonal motion (angle=45\u00b0):\n   - direction=0.0:   \u2199\u2022\u2197   (symmetric blur)\n   - direction=1.0:    \u2022\u2197   (forward diagonal blur)\n   - direction=-1.0:  \u2199\u2022    (backward diagonal blur)\n

Note

  • angle controls the orientation of the motion line
  • direction controls the distribution of the blur along that line
  • Together they can simulate various motion effects:
  • Camera shake: Small angle range + direction near 0
  • Object motion: Specific angle + direction=1.0
  • Complex motion: Random angle + random direction

Examples:

Python
>>> import albumentations as A\n>>> # Horizontal camera shake (symmetric)\n>>> transform = A.MotionBlur(\n...     angle_range=(-5, 5),      # Near-horizontal motion\n...     direction_range=(0, 0),    # Symmetric blur\n...     p=1.0\n... )\n>>>\n>>> # Object moving right\n>>> transform = A.MotionBlur(\n...     angle_range=(0, 0),        # Horizontal motion\n...     direction_range=(0.8, 1.0), # Strong forward bias\n...     p=1.0\n... )\n

References

  • Motion blur fundamentals: https://en.wikipedia.org/wiki/Motion_blur

  • Directional blur kernels: https://www.sciencedirect.com/topics/computer-science/directional-blur

  • OpenCV filter2D (used for convolution): https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04

  • Research on motion blur simulation: \"Understanding and Evaluating Blind Deconvolution Algorithms\" (CVPR 2009) https://doi.org/10.1109/CVPR.2009.5206815

  • Motion blur in photography: \"The Manual of Photography\", Chapter 7: Motion in Photography ISBN: 978-0240520377

  • Kornia's implementation (similar approach): https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomMotionBlur

See Also: - GaussianBlur: For uniform blur effects - MedianBlur: For noise reduction while preserving edges - RandomRain: Another motion-based effect - Perspective: For geometric motion-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class MotionBlur(Blur):\n    \"\"\"Apply motion blur to the input image using a directional kernel.\n\n    This transform simulates motion blur effects that occur during image capture,\n    such as camera shake or object movement. It creates a directional blur using\n    a line-shaped kernel with controllable angle, direction, and position.\n\n    Args:\n        blur_limit (int | tuple[int, int]): Maximum kernel size for blurring.\n            Should be in range [3, inf).\n            - If int: kernel size will be randomly chosen from [3, blur_limit]\n            - If tuple: kernel size will be randomly chosen from [min, max]\n            Larger values create stronger blur effects.\n            Default: (3, 7)\n\n        angle_range (tuple[float, float]): Range of possible angles in degrees.\n            Controls the rotation of the motion blur line:\n            - 0\u00b0: Horizontal motion blur \u2192\n            - 45\u00b0: Diagonal motion blur \u2197\n            - 90\u00b0: Vertical motion blur \u2191\n            - 135\u00b0: Diagonal motion blur \u2196\n            Default: (0, 360)\n\n        direction_range (tuple[float, float]): Range for motion bias.\n            Controls how the blur extends from the center:\n            - -1.0: Blur extends only backward (\u2190)\n            -  0.0: Blur extends equally in both directions (\u2190\u2192)\n            -  1.0: Blur extends only forward (\u2192)\n            For example, with angle=0:\n            - direction=-1.0: \u2190\u2022\n            - direction=0.0:  \u2190\u2022\u2192\n            - direction=1.0:   \u2022\u2192\n            Default: (-1.0, 1.0)\n\n        allow_shifted (bool): Allow random kernel position shifts.\n            - If True: Kernel can be randomly offset from center\n            - If False: Kernel will always be centered\n            Default: True\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Examples of angle vs direction:\n        1. Horizontal motion (angle=0\u00b0):\n           - direction=0.0:   \u2190\u2022\u2192   (symmetric blur)\n           - direction=1.0:    \u2022\u2192   (forward blur)\n           - direction=-1.0:  \u2190\u2022    (backward blur)\n\n        2. Vertical motion (angle=90\u00b0):\n           - direction=0.0:   \u2191\u2022\u2193   (symmetric blur)\n           - direction=1.0:    \u2022\u2191   (upward blur)\n           - direction=-1.0:  \u2193\u2022    (downward blur)\n\n        3. Diagonal motion (angle=45\u00b0):\n           - direction=0.0:   \u2199\u2022\u2197   (symmetric blur)\n           - direction=1.0:    \u2022\u2197   (forward diagonal blur)\n           - direction=-1.0:  \u2199\u2022    (backward diagonal blur)\n\n    Note:\n        - angle controls the orientation of the motion line\n        - direction controls the distribution of the blur along that line\n        - Together they can simulate various motion effects:\n          * Camera shake: Small angle range + direction near 0\n          * Object motion: Specific angle + direction=1.0\n          * Complex motion: Random angle + random direction\n\n    Example:\n        >>> import albumentations as A\n        >>> # Horizontal camera shake (symmetric)\n        >>> transform = A.MotionBlur(\n        ...     angle_range=(-5, 5),      # Near-horizontal motion\n        ...     direction_range=(0, 0),    # Symmetric blur\n        ...     p=1.0\n        ... )\n        >>>\n        >>> # Object moving right\n        >>> transform = A.MotionBlur(\n        ...     angle_range=(0, 0),        # Horizontal motion\n        ...     direction_range=(0.8, 1.0), # Strong forward bias\n        ...     p=1.0\n        ... )\n\n    References:\n        - Motion blur fundamentals:\n          https://en.wikipedia.org/wiki/Motion_blur\n\n        - Directional blur kernels:\n          https://www.sciencedirect.com/topics/computer-science/directional-blur\n\n        - OpenCV filter2D (used for convolution):\n          https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04\n\n        - Research on motion blur simulation:\n          \"Understanding and Evaluating Blind Deconvolution Algorithms\" (CVPR 2009)\n          https://doi.org/10.1109/CVPR.2009.5206815\n\n        - Motion blur in photography:\n          \"The Manual of Photography\", Chapter 7: Motion in Photography\n          ISBN: 978-0240520377\n\n        - Kornia's implementation (similar approach):\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomMotionBlur\n\n    See Also:\n        - GaussianBlur: For uniform blur effects\n        - MedianBlur: For noise reduction while preserving edges\n        - RandomRain: Another motion-based effect\n        - Perspective: For geometric motion-like distortions\n\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        allow_shifted: bool\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 360)),\n        ]\n        direction_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(min_val=-1.0, max_val=1.0)),\n        ]\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = 7,\n        allow_shifted: bool = True,\n        angle_range: tuple[float, float] = (0, 360),\n        direction_range: tuple[float, float] = (-1.0, 1.0),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(blur_limit=blur_limit, p=p)\n        self.allow_shifted = allow_shifted\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.angle_range = angle_range\n        self.direction_range = direction_range\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"allow_shifted\",\n            \"angle_range\",\n            \"direction_range\",\n        )\n\n    def apply(self, img: np.ndarray, kernel: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel=kernel)\n\n    def get_params(self) -> dict[str, Any]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n\n        angle = self.py_random.uniform(*self.angle_range)\n        direction = self.py_random.uniform(*self.direction_range)\n\n        # Create motion blur kernel\n        kernel = fblur.create_motion_kernel(\n            ksize,\n            angle,\n            direction,\n            allow_shifted=self.allow_shifted,\n            random_state=self.py_random,\n        )\n\n        return {\"kernel\": kernel.astype(np.float32) / np.sum(kernel)}\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.ZoomBlur","title":"class ZoomBlur (max_factor=(1, 1.31), step_factor=(0.01, 0.03), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply zoom blur transform.

Parameters:

Name Type Description max_factor float, float) or float

range for max factor for blurring. If max_factor is a single float, the range will be (1, limit). Default: (1, 1.31). All max_factor values should be larger than 1.

step_factor float, float) or float

If single float will be used as step parameter for np.arange. If tuple of float step_factor will be in range [step_factor[0], step_factor[1]). Default: (0.01, 0.03). All step_factor values should be positive.

p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: unit8, float32

Reference

https://arxiv.org/abs/1903.12261

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class ZoomBlur(ImageOnlyTransform):\n    \"\"\"Apply zoom blur transform.\n\n    Args:\n        max_factor ((float, float) or float): range for max factor for blurring.\n            If max_factor is a single float, the range will be (1, limit). Default: (1, 1.31).\n            All max_factor values should be larger than 1.\n        step_factor ((float, float) or float): If single float will be used as step parameter for np.arange.\n            If tuple of float step_factor will be in range `[step_factor[0], step_factor[1])`. Default: (0.01, 0.03).\n            All step_factor values should be positive.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        unit8, float32\n\n    Reference:\n        https://arxiv.org/abs/1903.12261\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        max_factor: OnePlusFloatRangeType\n        step_factor: NonNegativeFloatRangeType\n\n    def __init__(\n        self,\n        max_factor: ScaleFloatType = (1, 1.31),\n        step_factor: ScaleFloatType = (0.01, 0.03),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_factor = cast(tuple[float, float], max_factor)\n        self.step_factor = cast(tuple[float, float], step_factor)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        zoom_factors: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.zoom_blur(img, zoom_factors)\n\n    def get_params(self) -> dict[str, Any]:\n        step_factor = self.py_random.uniform(*self.step_factor)\n        max_factor = max(1 + step_factor, self.py_random.uniform(*self.max_factor))\n        return {\"zoom_factors\": np.arange(1.0, max_factor, step_factor)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"max_factor\", \"step_factor\")\n
"},{"location":"api_reference/augmentations/crops/","title":"Index","text":"
  • Crop functional transforms (albumentations.augmentations.crops.functional)
  • Crop transforms (albumentations.augmentations.crops.transforms)
"},{"location":"api_reference/augmentations/crops/functional/","title":"Crop functional transforms (augmentations.crops.functional)","text":""},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_and_pad_keypoints","title":"def crop_and_pad_keypoints (keypoints, crop_params=None, pad_params=None, image_shape=(0, 0), result_shape=(0, 0), keep_size=False) [view source on GitHub]","text":"

Crop and pad multiple keypoints simultaneously.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).

crop_params Sequence[int]

Crop parameters [crop_x1, crop_y1, ...].

pad_params Sequence[int]

Pad parameters [top, bottom, left, right].

image_shape Tuple[int, int]

Original image shape (rows, cols).

result_shape Tuple[int, int]

Result image shape (rows, cols).

keep_size bool

Whether to keep the original size.

Returns:

Type Description np.ndarray

Array of transformed keypoints with the same shape as input.

Source code in albumentations/augmentations/crops/functional.py Python
@handle_empty_array(\"keypoints\")\ndef crop_and_pad_keypoints(\n    keypoints: np.ndarray,\n    crop_params: tuple[int, int, int, int] | None = None,\n    pad_params: tuple[int, int, int, int] | None = None,\n    image_shape: tuple[int, int] = (0, 0),\n    result_shape: tuple[int, int] = (0, 0),\n    keep_size: bool = False,\n) -> np.ndarray:\n    \"\"\"Crop and pad multiple keypoints simultaneously.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).\n        crop_params (Sequence[int], optional): Crop parameters [crop_x1, crop_y1, ...].\n        pad_params (Sequence[int], optional): Pad parameters [top, bottom, left, right].\n        image_shape (Tuple[int, int]): Original image shape (rows, cols).\n        result_shape (Tuple[int, int]): Result image shape (rows, cols).\n        keep_size (bool): Whether to keep the original size.\n\n    Returns:\n        np.ndarray: Array of transformed keypoints with the same shape as input.\n    \"\"\"\n    transformed_keypoints = keypoints.copy()\n\n    if crop_params is not None:\n        crop_x1, crop_y1 = crop_params[:2]\n        transformed_keypoints[:, 0] -= crop_x1\n        transformed_keypoints[:, 1] -= crop_y1\n\n    if pad_params is not None:\n        top, _, left, _ = pad_params\n        transformed_keypoints[:, 0] += left\n        transformed_keypoints[:, 1] += top\n\n    rows, cols = image_shape[:2]\n    result_rows, result_cols = result_shape[:2]\n\n    if keep_size and (result_cols != cols or result_rows != rows):\n        scale_x = cols / result_cols\n        scale_y = rows / result_rows\n        return fgeometric.keypoints_scale(transformed_keypoints, scale_x, scale_y)\n\n    return transformed_keypoints\n
"},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_bboxes_by_coords","title":"def crop_bboxes_by_coords (bboxes, crop_coords, image_shape, normalized_input=True) [view source on GitHub]","text":"

Crop bounding boxes based on given crop coordinates.

This function adjusts bounding boxes to fit within a cropped image.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where each row is [x_min, y_min, x_max, y_max, ...]. The bounding box coordinates can be either normalized (in [0, 1]) if normalized_input=True or absolute pixel values if normalized_input=False.

crop_coords tuple[int, int, int, int]

Crop coordinates (x_min, y_min, x_max, y_max) in absolute pixel values.

image_shape tuple[int, int]

Original image shape (height, width).

normalized_input bool

Whether input boxes are in normalized coordinates. If True, assumes input is normalized [0,1] and returns normalized coordinates. If False, assumes input is in absolute pixels and returns absolute coordinates. Default: True for backward compatibility.

Returns:

Type Description np.ndarray

Array of cropped bounding boxes. Coordinates will be in the same format as input (normalized if normalized_input=True, absolute pixels if normalized_input=False).

Note

Bounding boxes that fall completely outside the crop area will be removed. Bounding boxes that partially overlap with the crop area will be adjusted to fit within it.

Source code in albumentations/augmentations/crops/functional.py Python
def crop_bboxes_by_coords(\n    bboxes: np.ndarray,\n    crop_coords: tuple[int, int, int, int],\n    image_shape: tuple[int, int],\n    normalized_input: bool = True,\n) -> np.ndarray:\n    \"\"\"Crop bounding boxes based on given crop coordinates.\n\n    This function adjusts bounding boxes to fit within a cropped image.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (N, 4+) where each row is\n                             [x_min, y_min, x_max, y_max, ...]. The bounding box coordinates\n                             can be either normalized (in [0, 1]) if normalized_input=True or\n                             absolute pixel values if normalized_input=False.\n        crop_coords (tuple[int, int, int, int]): Crop coordinates (x_min, y_min, x_max, y_max)\n                                                 in absolute pixel values.\n        image_shape (tuple[int, int]): Original image shape (height, width).\n        normalized_input (bool): Whether input boxes are in normalized coordinates.\n                               If True, assumes input is normalized [0,1] and returns normalized coordinates.\n                               If False, assumes input is in absolute pixels and returns absolute coordinates.\n                               Default: True for backward compatibility.\n\n    Returns:\n        np.ndarray: Array of cropped bounding boxes. Coordinates will be in the same format as input\n                   (normalized if normalized_input=True, absolute pixels if normalized_input=False).\n\n    Note:\n        Bounding boxes that fall completely outside the crop area will be removed.\n        Bounding boxes that partially overlap with the crop area will be adjusted to fit within it.\n    \"\"\"\n    if not bboxes.size:\n        return bboxes\n\n    # Convert to absolute coordinates if needed\n    if normalized_input:\n        cropped_bboxes = denormalize_bboxes(bboxes.copy().astype(np.float32), image_shape)\n    else:\n        cropped_bboxes = bboxes.copy().astype(np.float32)\n\n    x_min, y_min = crop_coords[:2]\n\n    # Subtract crop coordinates\n    cropped_bboxes[:, [0, 2]] -= x_min\n    cropped_bboxes[:, [1, 3]] -= y_min\n\n    # Calculate crop shape\n    crop_height = crop_coords[3] - crop_coords[1]\n    crop_width = crop_coords[2] - crop_coords[0]\n    crop_shape = (crop_height, crop_width)\n\n    # Return in same format as input\n    return normalize_bboxes(cropped_bboxes, crop_shape) if normalized_input else cropped_bboxes\n
"},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_keypoints_by_coords","title":"def crop_keypoints_by_coords (keypoints, crop_coords) [view source on GitHub]","text":"

Crop keypoints using the provided coordinates of bottom-left and top-right corners in pixels.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).

crop_coords tuple

Crop box coords (x1, y1, x2, y2).

Returns:

Type Description np.ndarray

An array of cropped keypoints with the same shape as the input.

Source code in albumentations/augmentations/crops/functional.py Python
@handle_empty_array(\"keypoints\")\ndef crop_keypoints_by_coords(\n    keypoints: np.ndarray,\n    crop_coords: tuple[int, int, int, int],\n) -> np.ndarray:\n    \"\"\"Crop keypoints using the provided coordinates of bottom-left and top-right corners in pixels.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).\n        crop_coords (tuple): Crop box coords (x1, y1, x2, y2).\n\n    Returns:\n        np.ndarray: An array of cropped keypoints with the same shape as the input.\n    \"\"\"\n    x1, y1 = crop_coords[:2]\n\n    cropped_keypoints = keypoints.copy()\n    cropped_keypoints[:, 0] -= x1  # Adjust x coordinates\n    cropped_keypoints[:, 1] -= y1  # Adjust y coordinates\n\n    return cropped_keypoints\n
"},{"location":"api_reference/augmentations/crops/transforms/","title":"Crop transforms (augmentations.crops.transforms)","text":""},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.AtLeastOneBBoxRandomCrop","title":"class AtLeastOneBBoxRandomCrop (height, width, erosion_factor=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crops an image to a fixed resolution, while ensuring that at least one bounding box is always in the crop. The maximal erosion factor define by how much the target bounding box can be thinned out. For example, erosion_factor = 0.2 means that the bounding box dimensions can be thinned by up to 20%.

Parameters:

Name Type Description height int

Height of the crop.

width int

Width of the crop.

erosion_factor float

Maximal erosion factor of the height and width of the target bounding box. Default: 0.0.

p float

The probability of applying the transform. Default: 1.0.

always_apply bool | None

Whether to apply the transform systematically.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class AtLeastOneBBoxRandomCrop(BaseCrop):\n    \"\"\"Crops an image to a fixed resolution, while ensuring that at least one bounding box is always in the crop.\n    The maximal erosion factor define by how much the target bounding box can be thinned out.\n    For example, erosion_factor = 0.2 means that the bounding box dimensions can be thinned by up to 20%.\n\n    Args:\n        height: Height of the crop.\n        width: Width of the crop.\n        erosion_factor: Maximal erosion factor of the height and width of the target bounding box. Default: 0.0.\n        p: The probability of applying the transform. Default: 1.0.\n        always_apply: Whether to apply the transform systematically.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseCrop.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        erosion_factor: Annotated[float, Field(ge=0.0, le=1.0)]\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        erosion_factor: float = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.erosion_factor = erosion_factor\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_height, image_width = params[\"shape\"][:2]\n        bboxes = data.get(\"bboxes\", [])\n\n        if self.height > image_height or self.width > image_width:\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_height, image_width}\",\n            )\n\n        if len(bboxes) > 0:\n            # Pick a bbox amongst all possible as our reference bbox.\n            bboxes = denormalize_bboxes(bboxes, image_shape=(image_height, image_width))\n            bbox = self.py_random.choice(bboxes)\n\n            x1, y1, x2, y2 = bbox[:4]\n\n            w = x2 - x1\n            h = y2 - y1\n\n            # Compute the eroded width and height\n            ew = w * (1.0 - self.erosion_factor)\n            eh = h * (1.0 - self.erosion_factor)\n\n            # Compute the lower and upper bounds for the x-axis and y-axis.\n            ax1 = np.clip(\n                a=x1 + ew - self.width,\n                a_min=0.0,\n                a_max=image_width - self.width,\n            )\n            bx1 = np.clip(\n                a=x2 - ew,\n                a_min=0.0,\n                a_max=image_width - self.width,\n            )\n\n            ay1 = np.clip(\n                a=y1 + eh - self.height,\n                a_min=0.0,\n                a_max=image_height - self.height,\n            )\n            by1 = np.clip(\n                a=y2 - eh,\n                a_min=0.0,\n                a_max=image_height - self.height,\n            )\n        else:\n            # If there are no bboxes, just crop anywhere in the image.\n            ax1 = 0.0\n            bx1 = image_width - self.width\n\n            ay1 = 0.0\n            by1 = image_height - self.height\n\n        # Randomly draw the upper-left corner.\n        x1 = int(self.py_random.uniform(a=ax1, b=bx1))\n        y1 = int(self.py_random.uniform(a=ay1, b=by1))\n\n        x2 = x1 + self.width\n        y2 = y1 + self.height\n\n        return {\"crop_coords\": (x1, y1, x2, y2)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"erosion_factor\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BBoxSafeRandomCrop","title":"class BBoxSafeRandomCrop (erosion_rate=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input without loss of bounding boxes.

This transform performs a random crop of the input image while ensuring that all bounding boxes remain within the cropped area. It's particularly useful for object detection tasks where preserving all objects in the image is crucial.

Parameters:

Name Type Description erosion_rate float

A value between 0.0 and 1.0 that determines the minimum allowable size of the crop as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be at least 80% of the original image height. Default: 0.0 (no minimum size).

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

This transform ensures that all bounding boxes in the original image are fully contained within the cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out), it will default to cropping the entire image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.ones((300, 300, 3), dtype=np.uint8)\n>>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n>>> transform = A.Compose([\n...     A.BBoxSafeRandomCrop(erosion_rate=0.2, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n>>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n>>> transformed_image = transformed['image']\n>>> transformed_bboxes = transformed['bboxes']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BBoxSafeRandomCrop(BaseCrop):\n    \"\"\"Crop a random part of the input without loss of bounding boxes.\n\n    This transform performs a random crop of the input image while ensuring that all bounding boxes remain within\n    the cropped area. It's particularly useful for object detection tasks where preserving all objects in the image\n    is crucial.\n\n    Args:\n        erosion_rate (float): A value between 0.0 and 1.0 that determines the minimum allowable size of the crop\n            as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be\n            at least 80% of the original image height. Default: 0.0 (no minimum size).\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transform ensures that all bounding boxes in the original image are fully contained within the\n        cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out),\n        it will default to cropping the entire image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.ones((300, 300, 3), dtype=np.uint8)\n        >>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n        >>> transform = A.Compose([\n        ...     A.BBoxSafeRandomCrop(erosion_rate=0.2, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n        >>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n        >>> transformed_image = transformed['image']\n        >>> transformed_bboxes = transformed['bboxes']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        erosion_rate: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n\n    def __init__(self, erosion_rate: float = 0.0, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p)\n        self.erosion_rate = erosion_rate\n\n    def _get_coords_no_bbox(self, image_shape: tuple[int, int]) -> tuple[int, int, int, int]:\n        image_height, image_width = image_shape\n\n        erosive_h = int(image_height * (1.0 - self.erosion_rate))\n        crop_height = image_height if erosive_h >= image_height else self.py_random.randint(erosive_h, image_height)\n\n        crop_width = int(crop_height * image_width / image_height)\n\n        h_start = self.py_random.random()\n        w_start = self.py_random.random()\n\n        crop_shape = (crop_height, crop_width)\n\n        return fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n\n        if len(data[\"bboxes\"]) == 0:  # less likely, this class is for use with bboxes.\n            crop_coords = self._get_coords_no_bbox(image_shape)\n            return {\"crop_coords\": crop_coords}\n\n        bbox_union = union_of_bboxes(bboxes=data[\"bboxes\"], erosion_rate=self.erosion_rate)\n\n        if bbox_union is None:\n            crop_coords = self._get_coords_no_bbox(image_shape)\n            return {\"crop_coords\": crop_coords}\n\n        x_min, y_min, x_max, y_max = bbox_union\n\n        x_min = np.clip(x_min, 0, 1)\n        y_min = np.clip(y_min, 0, 1)\n        x_max = np.clip(x_max, x_min, 1)\n        y_max = np.clip(y_max, y_min, 1)\n\n        image_height, image_width = image_shape\n\n        crop_x_min = int(x_min * self.py_random.random() * image_width)\n        crop_y_min = int(y_min * self.py_random.random() * image_height)\n\n        bbox_xmax = x_max + (1 - x_max) * self.py_random.random()\n        bbox_ymax = y_max + (1 - y_max) * self.py_random.random()\n        crop_x_max = int(bbox_xmax * image_width)\n        crop_y_max = int(bbox_ymax * image_height)\n\n        return {\"crop_coords\": (crop_x_min, crop_y_min, crop_x_max, crop_y_max)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"erosion_rate\",)\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseCrop","title":"class BaseCrop [view source on GitHub]","text":"

Base class for transforms that only perform cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseCrop(DualTransform):\n    \"\"\"Base class for transforms that only perform cropping.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop(img, x_min=crop_coords[0], y_min=crop_coords[1], x_max=crop_coords[2], y_max=crop_coords[3])\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_bboxes_by_coords(bboxes, crop_coords, params[\"shape\"][:2])\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_keypoints_by_coords(keypoints, crop_coords)\n\n    @staticmethod\n    def _clip_bbox(bbox: tuple[int, int, int, int], image_shape: tuple[int, int]) -> tuple[int, int, int, int]:\n        height, width = image_shape[:2]\n        x_min, y_min, x_max, y_max = bbox\n        x_min = np.clip(x_min, 0, width)\n        y_min = np.clip(y_min, 0, height)\n\n        x_max = np.clip(x_max, x_min, width)\n        y_max = np.clip(y_max, y_min, height)\n        return x_min, y_min, x_max, y_max\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseCropAndPad","title":"class BaseCropAndPad (pad_if_needed, border_mode, fill, fill_mask, pad_position, p, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that need both cropping and padding.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseCropAndPad(BaseCrop):\n    \"\"\"Base class for transforms that need both cropping and padding.\"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        pad_if_needed: bool\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_position: PositionType\n\n    def __init__(\n        self,\n        pad_if_needed: bool,\n        border_mode: int,\n        fill: ColorType,\n        fill_mask: ColorType,\n        pad_position: PositionType,\n        p: float,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p)\n        self.pad_if_needed = pad_if_needed\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.pad_position = pad_position\n\n    def _get_pad_params(self, image_shape: tuple[int, int], target_shape: tuple[int, int]) -> dict[str, Any] | None:\n        \"\"\"Calculate padding parameters if needed.\"\"\"\n        if not self.pad_if_needed:\n            return None\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=image_shape,\n            min_height=target_shape[0],\n            min_width=target_shape[1],\n            pad_height_divisor=None,\n            pad_width_divisor=None,\n        )\n\n        if h_pad_top == h_pad_bottom == w_pad_left == w_pad_right == 0:\n            return None\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.pad_position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        if pad_params is not None:\n            img = fgeometric.pad_with_params(\n                img,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                border_mode=self.border_mode,\n                value=self.fill,\n            )\n        return BaseCrop.apply(self, img, crop_coords, **params)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_coords: Any,\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        if pad_params is not None:\n            mask = fgeometric.pad_with_params(\n                mask,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                border_mode=self.border_mode,\n                value=self.fill_mask,\n            )\n        # Note' that super().apply would apply the padding twice as it is looped to this.apply\n        return BaseCrop.apply(self, mask, crop_coords=crop_coords, **params)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        image_shape = params[\"shape\"][:2]\n\n        if pad_params is not None:\n            # First denormalize bboxes to absolute coordinates\n            bboxes_np = denormalize_bboxes(bboxes, image_shape)\n\n            # Apply padding to bboxes (already works with absolute coordinates)\n            bboxes_np = fgeometric.pad_bboxes(\n                bboxes_np,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                self.border_mode,\n                image_shape=image_shape,\n            )\n\n            # Update shape to padded dimensions\n            padded_height = image_shape[0] + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            padded_width = image_shape[1] + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n            padded_shape = (padded_height, padded_width)\n\n            bboxes_np = normalize_bboxes(bboxes_np, padded_shape)\n\n            params[\"shape\"] = padded_shape\n\n            return BaseCrop.apply_to_bboxes(self, bboxes_np, crop_coords, **params)\n\n        # If no padding, use original function behavior\n        return BaseCrop.apply_to_bboxes(self, bboxes, crop_coords, **params)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        image_shape = params[\"shape\"][:2]\n\n        if pad_params is not None:\n            # Calculate padded dimensions\n            padded_height = image_shape[0] + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            padded_width = image_shape[1] + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n            # First apply padding to keypoints using original image shape\n            keypoints = fgeometric.pad_keypoints(\n                keypoints,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                self.border_mode,\n                image_shape=image_shape,\n            )\n\n            # Update image shape for subsequent crop operation\n            params = {**params, \"shape\": (padded_height, padded_width)}\n\n        return BaseCrop.apply_to_keypoints(self, keypoints, crop_coords, **params)\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseRandomSizedCropInitSchema","title":"class BaseRandomSizedCropInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseRandomSizedCropInitSchema(BaseTransformInitSchema):\n    size: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CenterCrop","title":"class CenterCrop (height, width, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0.0, fill_mask=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop the central part of the input.

This transform crops the center of the input image, mask, bounding boxes, and keypoints to the specified dimensions. It's useful when you want to focus on the central region of the input, discarding peripheral information.

Parameters:

Name Type Description height int

The height of the crop. Must be greater than 0.

width int

The width of the crop. Must be greater than 0.

pad_if_needed bool

Whether to pad if crop size exceeds image size. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value for images if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks if border_mode is cv2.BORDER_CONSTANT. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If pad_if_needed is False and crop size exceeds image dimensions, it will raise a CropSizeError.
  • If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded.
  • For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CenterCrop(BaseCropAndPad):\n    \"\"\"Crop the central part of the input.\n\n    This transform crops the center of the input image, mask, bounding boxes, and keypoints to the specified dimensions.\n    It's useful when you want to focus on the central region of the input, discarding peripheral information.\n\n    Args:\n        height (int): The height of the crop. Must be greater than 0.\n        width (int): The width of the crop. Must be greater than 0.\n        pad_if_needed (bool): Whether to pad if crop size exceeds image size. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value for images if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If pad_if_needed is False and crop size exceeds image dimensions, it will raise a CropSizeError.\n        - If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded.\n        - For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_dimensions(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n            return self\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0.0,\n        fill_mask: ColorType = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.height = height\n        self.width = width\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"height\",\n            \"width\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        if not self.pad_if_needed and (self.height > image_height or self.width > image_width):\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_shape[:2]}\",\n            )\n\n        # Get padding params first if needed\n        pad_params = self._get_pad_params(image_shape, (self.height, self.width))\n\n        # If padding is needed, adjust the image shape for crop calculation\n        if pad_params is not None:\n            pad_top = pad_params[\"pad_top\"]\n            pad_bottom = pad_params[\"pad_bottom\"]\n            pad_left = pad_params[\"pad_left\"]\n            pad_right = pad_params[\"pad_right\"]\n\n            padded_height = image_height + pad_top + pad_bottom\n            padded_width = image_width + pad_left + pad_right\n            padded_shape = (padded_height, padded_width)\n\n            # Get crop coordinates based on padded dimensions\n            crop_coords = fcrops.get_center_crop_coords(padded_shape, (self.height, self.width))\n        else:\n            # Get crop coordinates based on original dimensions\n            crop_coords = fcrops.get_center_crop_coords(image_shape, (self.height, self.width))\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.Crop","title":"class Crop (x_min=0, y_min=0, x_max=1024, y_max=1024, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a specific region from the input image.

This transform crops a rectangular region from the input image, mask, bounding boxes, and keypoints based on specified coordinates. It's useful when you want to extract a specific area of interest from your inputs.

Parameters:

Name Type Description x_min int

Minimum x-coordinate of the crop region (left edge). Must be >= 0. Default: 0.

y_min int

Minimum y-coordinate of the crop region (top edge). Must be >= 0. Default: 0.

x_max int

Maximum x-coordinate of the crop region (right edge). Must be > x_min. Default: 1024.

y_max int

Maximum y-coordinate of the crop region (bottom edge). Must be > y_min. Default: 1024.

pad_if_needed bool

Whether to pad if crop coordinates exceed image dimensions. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The crop coordinates are applied as follows: x_min <= x < x_max and y_min <= y < y_max.
  • If pad_if_needed is False and crop region extends beyond image boundaries, it will be clipped.
  • If pad_if_needed is True, image will be padded to accommodate the full crop region.
  • For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class Crop(BaseCropAndPad):\n    \"\"\"Crop a specific region from the input image.\n\n    This transform crops a rectangular region from the input image, mask, bounding boxes, and keypoints\n    based on specified coordinates. It's useful when you want to extract a specific area of interest\n    from your inputs.\n\n    Args:\n        x_min (int): Minimum x-coordinate of the crop region (left edge). Must be >= 0. Default: 0.\n        y_min (int): Minimum y-coordinate of the crop region (top edge). Must be >= 0. Default: 0.\n        x_max (int): Maximum x-coordinate of the crop region (right edge). Must be > x_min. Default: 1024.\n        y_max (int): Maximum y-coordinate of the crop region (bottom edge). Must be > y_min. Default: 1024.\n        pad_if_needed (bool): Whether to pad if crop coordinates exceed image dimensions. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The crop coordinates are applied as follows: x_min <= x < x_max and y_min <= y < y_max.\n        - If pad_if_needed is False and crop region extends beyond image boundaries, it will be clipped.\n        - If pad_if_needed is True, image will be padded to accommodate the full crop region.\n        - For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        x_min: Annotated[int, Field(ge=0)]\n        y_min: Annotated[int, Field(ge=0)]\n        x_max: Annotated[int, Field(gt=0)]\n        y_max: Annotated[int, Field(gt=0)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_coordinates(self) -> Self:\n            if not self.x_min < self.x_max:\n                msg = \"x_max must be greater than x_min\"\n                raise ValueError(msg)\n            if not self.y_min < self.y_max:\n                msg = \"y_max must be greater than y_min\"\n                raise ValueError(msg)\n\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n\n            return self\n\n    def __init__(\n        self,\n        x_min: int = 0,\n        y_min: int = 0,\n        x_max: int = 1024,\n        y_max: int = 1024,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.x_min = x_min\n        self.y_min = y_min\n        self.x_max = x_max\n        self.y_max = y_max\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        crop_height = self.y_max - self.y_min\n        crop_width = self.x_max - self.x_min\n\n        if not self.pad_if_needed:\n            # If no padding, clip coordinates to image boundaries\n            x_min = np.clip(self.x_min, 0, image_width)\n            y_min = np.clip(self.y_min, 0, image_height)\n            x_max = np.clip(self.x_max, x_min, image_width)\n            y_max = np.clip(self.y_max, y_min, image_height)\n            return {\"crop_coords\": (x_min, y_min, x_max, y_max)}\n\n        # Calculate padding if needed\n        pad_params = self._get_pad_params(\n            image_shape=image_shape,\n            target_shape=(max(crop_height, image_height), max(crop_width, image_width)),\n        )\n\n        if pad_params is not None:\n            # Adjust crop coordinates based on padding\n            x_min = self.x_min + pad_params[\"pad_left\"]\n            y_min = self.y_min + pad_params[\"pad_top\"]\n            x_max = self.x_max + pad_params[\"pad_left\"]\n            y_max = self.y_max + pad_params[\"pad_top\"]\n            crop_coords = (x_min, y_min, x_max, y_max)\n        else:\n            crop_coords = (self.x_min, self.y_min, self.x_max, self.y_max)\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"x_min\",\n            \"y_min\",\n            \"x_max\",\n            \"y_max\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CropAndPad","title":"class CropAndPad (px=None, percent=None, pad_mode=None, pad_cval=None, pad_cval_mask=None, keep_size=True, sample_independently=True, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop and pad images by pixel amounts or fractions of image sizes.

This transform allows for simultaneous cropping and padding of images. Cropping removes pixels from the sides (i.e., extracts a subimage), while padding adds pixels to the sides (e.g., black pixels). The amount of cropping/padding can be specified either in absolute pixels or as a fraction of the image size.

Parameters:

Name Type Description px int, tuple of int, tuple of tuples of int, or None

The number of pixels to crop (negative values) or pad (positive values) on each side of the image. Either this or the parameter percent may be set, not both at the same time. - If int: crop/pad all sides by this value. - If tuple of 2 ints: crop/pad by (top/bottom, left/right). - If tuple of 4 ints: crop/pad by (top, right, bottom, left). - Each int can also be a tuple of 2 ints for a range, or a list of ints for discrete choices. Default: None.

percent float, tuple of float, tuple of tuples of float, or None

The fraction of the image size to crop (negative values) or pad (positive values) on each side. Either this or the parameter px may be set, not both at the same time. - If float: crop/pad all sides by this fraction. - If tuple of 2 floats: crop/pad by (top/bottom, left/right) fractions. - If tuple of 4 floats: crop/pad by (top, right, bottom, left) fractions. - Each float can also be a tuple of 2 floats for a range, or a list of floats for discrete choices. Default: None.

border_mode int

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

The constant value to use for padding if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Same as fill but used for mask padding. Default: 0.

keep_size bool

If True, the output image will be resized to the input image size after cropping/padding. Default: True.

sample_independently bool

If True and ranges are used for px/percent, sample a value for each side independently. If False, sample one value and use it for all sides. Default: True.

interpolation int

OpenCV interpolation flag used for resizing if keep_size is True. Default: cv2.INTER_LINEAR.

mask_interpolation int

OpenCV interpolation flag used for resizing if keep_size is True. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform will never crop images below a height or width of 1.
  • When using pixel values (px), the image will be cropped/padded by exactly that many pixels.
  • When using percentages (percent), the amount of crop/pad will be calculated based on the image size.
  • Bounding boxes that end up fully outside the image after cropping will be removed.
  • Keypoints that end up outside the image after cropping will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.CropAndPad(px=(-10, 20, 30, -40), border_mode=cv2.BORDER_REFLECT, fill=128, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CropAndPad(DualTransform):\n    \"\"\"Crop and pad images by pixel amounts or fractions of image sizes.\n\n    This transform allows for simultaneous cropping and padding of images. Cropping removes pixels from the sides\n    (i.e., extracts a subimage), while padding adds pixels to the sides (e.g., black pixels). The amount of\n    cropping/padding can be specified either in absolute pixels or as a fraction of the image size.\n\n    Args:\n        px (int, tuple of int, tuple of tuples of int, or None):\n            The number of pixels to crop (negative values) or pad (positive values) on each side of the image.\n            Either this or the parameter `percent` may be set, not both at the same time.\n            - If int: crop/pad all sides by this value.\n            - If tuple of 2 ints: crop/pad by (top/bottom, left/right).\n            - If tuple of 4 ints: crop/pad by (top, right, bottom, left).\n            - Each int can also be a tuple of 2 ints for a range, or a list of ints for discrete choices.\n            Default: None.\n\n        percent (float, tuple of float, tuple of tuples of float, or None):\n            The fraction of the image size to crop (negative values) or pad (positive values) on each side.\n            Either this or the parameter `px` may be set, not both at the same time.\n            - If float: crop/pad all sides by this fraction.\n            - If tuple of 2 floats: crop/pad by (top/bottom, left/right) fractions.\n            - If tuple of 4 floats: crop/pad by (top, right, bottom, left) fractions.\n            - Each float can also be a tuple of 2 floats for a range, or a list of floats for discrete choices.\n            Default: None.\n\n        border_mode (int):\n            OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n\n        fill (ColorType):\n            The constant value to use for padding if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n\n        fill_mask (ColorType):\n            Same as fill but used for mask padding. Default: 0.\n\n        keep_size (bool):\n            If True, the output image will be resized to the input image size after cropping/padding.\n            Default: True.\n\n        sample_independently (bool):\n            If True and ranges are used for px/percent, sample a value for each side independently.\n            If False, sample one value and use it for all sides. Default: True.\n\n        interpolation (int):\n            OpenCV interpolation flag used for resizing if keep_size is True.\n            Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (int):\n            OpenCV interpolation flag used for resizing if keep_size is True.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float):\n            Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform will never crop images below a height or width of 1.\n        - When using pixel values (px), the image will be cropped/padded by exactly that many pixels.\n        - When using percentages (percent), the amount of crop/pad will be calculated based on the image size.\n        - Bounding boxes that end up fully outside the image after cropping will be removed.\n        - Keypoints that end up outside the image after cropping will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.CropAndPad(px=(-10, 20, 30, -40), border_mode=cv2.BORDER_REFLECT, fill=128, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        px: PxType | None\n        percent: PercentType | None\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n        keep_size: bool\n        sample_independently: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def check_px_percent(self) -> Self:\n            if self.px is None and self.percent is None:\n                msg = \"Both px and percent parameters cannot be None simultaneously.\"\n                raise ValueError(msg)\n            if self.px is not None and self.percent is not None:\n                msg = \"Only px or percent may be set!\"\n                raise ValueError(msg)\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n\n            return self\n\n    def __init__(\n        self,\n        px: int | list[int] | None = None,\n        percent: float | list[float] | None = None,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        keep_size: bool = True,\n        sample_independently: bool = True,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.px = px\n        self.percent = percent\n\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n        self.keep_size = keep_size\n        self.sample_independently = sample_independently\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_params: Sequence[int],\n        pad_params: Sequence[int],\n        fill: ColorType,\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad(\n            img,\n            crop_params,\n            pad_params,\n            fill,\n            params[\"shape\"][:2],\n            self.interpolation,\n            self.border_mode,\n            self.keep_size,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_params: Sequence[int],\n        pad_params: Sequence[int],\n        fill_mask: ColorType,\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad(\n            mask,\n            crop_params,\n            pad_params,\n            fill_mask,\n            params[\"shape\"][:2],\n            self.mask_interpolation,\n            self.border_mode,\n            self.keep_size,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_params: tuple[int, int, int, int],\n        pad_params: tuple[int, int, int, int],\n        result_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad_bboxes(bboxes, crop_params, pad_params, params[\"shape\"][:2], result_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_params: tuple[int, int, int, int],\n        pad_params: tuple[int, int, int, int],\n        result_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad_keypoints(\n            keypoints,\n            crop_params,\n            pad_params,\n            params[\"shape\"][:2],\n            result_shape,\n            self.keep_size,\n        )\n\n    @staticmethod\n    def __prevent_zero(val1: int, val2: int, max_val: int) -> tuple[int, int]:\n        regain = abs(max_val) + 1\n        regain1 = regain // 2\n        regain2 = regain // 2\n        if regain1 + regain2 < regain:\n            regain1 += 1\n\n        if regain1 > val1:\n            diff = regain1 - val1\n            regain1 = val1\n            regain2 += diff\n        elif regain2 > val2:\n            diff = regain2 - val2\n            regain2 = val2\n            regain1 += diff\n\n        return val1 - regain1, val2 - regain2\n\n    @staticmethod\n    def _prevent_zero(crop_params: list[int], height: int, width: int) -> list[int]:\n        top, right, bottom, left = crop_params\n\n        remaining_height = height - (top + bottom)\n        remaining_width = width - (left + right)\n\n        if remaining_height < 1:\n            top, bottom = CropAndPad.__prevent_zero(top, bottom, height)\n        if remaining_width < 1:\n            left, right = CropAndPad.__prevent_zero(left, right, width)\n\n        return [max(top, 0), max(right, 0), max(bottom, 0), max(left, 0)]\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        if self.px is not None:\n            new_params = self._get_px_params()\n        else:\n            percent_params = self._get_percent_params()\n            new_params = [\n                int(percent_params[0] * height),\n                int(percent_params[1] * width),\n                int(percent_params[2] * height),\n                int(percent_params[3] * width),\n            ]\n\n        pad_params = [max(i, 0) for i in new_params]\n\n        crop_params = self._prevent_zero([-min(i, 0) for i in new_params], height, width)\n\n        top, right, bottom, left = crop_params\n        crop_params = [left, top, width - right, height - bottom]\n        result_rows = crop_params[3] - crop_params[1]\n        result_cols = crop_params[2] - crop_params[0]\n        if result_cols == width and result_rows == height:\n            crop_params = []\n\n        top, right, bottom, left = pad_params\n        pad_params = [top, bottom, left, right]\n        if any(pad_params):\n            result_rows += top + bottom\n            result_cols += left + right\n        else:\n            pad_params = []\n\n        return {\n            \"crop_params\": crop_params or None,\n            \"pad_params\": pad_params or None,\n            \"fill\": None if pad_params is None else self._get_pad_value(cast(ColorType, self.fill)),\n            \"fill_mask\": None if pad_params is None else self._get_pad_value(cast(ColorType, self.fill_mask)),\n            \"result_shape\": (result_rows, result_cols),\n        }\n\n    def _get_px_params(self) -> list[int]:\n        if self.px is None:\n            msg = \"px is not set\"\n            raise ValueError(msg)\n\n        if isinstance(self.px, int):\n            params = [self.px] * 4\n        elif len(self.px) == PAIR:\n            if self.sample_independently:\n                params = [self.py_random.randrange(*self.px) for _ in range(4)]\n            else:\n                px = self.py_random.randrange(*self.px)\n                params = [px] * 4\n        elif isinstance(self.px[0], int):\n            params = self.px\n        elif len(self.px[0]) == PAIR:\n            params = [self.py_random.randrange(*i) for i in self.px]\n        else:\n            params = [self.py_random.choice(i) for i in self.px]\n\n        return params\n\n    def _get_percent_params(self) -> list[float]:\n        if self.percent is None:\n            msg = \"percent is not set\"\n            raise ValueError(msg)\n\n        if isinstance(self.percent, float):\n            params = [self.percent] * 4\n        elif len(self.percent) == PAIR:\n            if self.sample_independently:\n                params = [self.py_random.uniform(*self.percent) for _ in range(4)]\n            else:\n                px = self.py_random.uniform(*self.percent)\n                params = [px] * 4\n        elif isinstance(self.percent[0], (int, float)):\n            params = self.percent\n        elif len(self.percent[0]) == PAIR:\n            params = [self.py_random.uniform(*i) for i in self.percent]\n        else:\n            params = [self.py_random.choice(i) for i in self.percent]\n\n        return params  # params = [top, right, bottom, left]\n\n    def _get_pad_value(\n        self,\n        fill: ColorType,\n    ) -> int | float:\n        if isinstance(fill, (list, tuple)):\n            if len(fill) == PAIR:\n                a, b = fill\n                if isinstance(a, int) and isinstance(b, int):\n                    return self.py_random.randint(a, b)\n                return self.py_random.uniform(a, b)\n            return self.py_random.choice(fill)\n\n        if isinstance(fill, Real):\n            return fill\n\n        msg = \"fill should be a number or list, or tuple of two numbers.\"\n        raise ValueError(msg)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"px\",\n            \"percent\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"keep_size\",\n            \"sample_independently\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CropNonEmptyMaskIfExists","title":"class CropNonEmptyMaskIfExists (height, width, ignore_values=None, ignore_channels=None, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop area with mask if mask is non-empty, else make random crop.

This transform attempts to crop a region containing a mask (non-zero pixels). If the mask is empty or not provided, it falls back to a random crop. This is particularly useful for segmentation tasks where you want to focus on regions of interest defined by the mask.

Parameters:

Name Type Description height int

Vertical size of crop in pixels. Must be > 0.

width int

Horizontal size of crop in pixels. Must be > 0.

ignore_values list of int

Values to ignore in mask, 0 values are always ignored. For example, if background value is 5, set ignore_values=[5] to ignore it. Default: None.

ignore_channels list of int

Channels to ignore in mask. For example, if background is the first channel, set ignore_channels=[0] to ignore it. Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If a mask is provided, the transform will try to crop an area containing non-zero (or non-ignored) pixels.
  • If no suitable area is found in the mask or no mask is provided, it will perform a random crop.
  • The crop size (height, width) must not exceed the original image dimensions.
  • Bounding boxes and keypoints are also cropped along with the image and mask.

Exceptions:

Type Description ValueError

If the specified crop size is larger than the input image dimensions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.zeros((100, 100), dtype=np.uint8)\n>>> mask[25:75, 25:75] = 1  # Create a non-empty region in the mask\n>>> transform = A.Compose([\n...     A.CropNonEmptyMaskIfExists(height=50, width=50, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n# The resulting crop will likely include part of the non-zero region in the mask\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CropNonEmptyMaskIfExists(BaseCrop):\n    \"\"\"Crop area with mask if mask is non-empty, else make random crop.\n\n    This transform attempts to crop a region containing a mask (non-zero pixels). If the mask is empty or not provided,\n    it falls back to a random crop. This is particularly useful for segmentation tasks where you want to focus on\n    regions of interest defined by the mask.\n\n    Args:\n        height (int): Vertical size of crop in pixels. Must be > 0.\n        width (int): Horizontal size of crop in pixels. Must be > 0.\n        ignore_values (list of int, optional): Values to ignore in mask, `0` values are always ignored.\n            For example, if background value is 5, set `ignore_values=[5]` to ignore it. Default: None.\n        ignore_channels (list of int, optional): Channels to ignore in mask.\n            For example, if background is the first channel, set `ignore_channels=[0]` to ignore it. Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If a mask is provided, the transform will try to crop an area containing non-zero (or non-ignored) pixels.\n        - If no suitable area is found in the mask or no mask is provided, it will perform a random crop.\n        - The crop size (height, width) must not exceed the original image dimensions.\n        - Bounding boxes and keypoints are also cropped along with the image and mask.\n\n    Raises:\n        ValueError: If the specified crop size is larger than the input image dimensions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.zeros((100, 100), dtype=np.uint8)\n        >>> mask[25:75, 25:75] = 1  # Create a non-empty region in the mask\n        >>> transform = A.Compose([\n        ...     A.CropNonEmptyMaskIfExists(height=50, width=50, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        # The resulting crop will likely include part of the non-zero region in the mask\n    \"\"\"\n\n    class InitSchema(BaseCrop.InitSchema):\n        ignore_values: list[int] | None\n        ignore_channels: list[int] | None\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        ignore_values: list[int] | None = None,\n        ignore_channels: list[int] | None = None,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p)\n\n        self.height = height\n        self.width = width\n        self.ignore_values = ignore_values\n        self.ignore_channels = ignore_channels\n\n    def _preprocess_mask(self, mask: np.ndarray) -> np.ndarray:\n        mask_height, mask_width = mask.shape[:2]\n\n        if self.ignore_values is not None:\n            ignore_values_np = np.array(self.ignore_values)\n            mask = np.where(np.isin(mask, ignore_values_np), 0, mask)\n\n        if mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS and self.ignore_channels is not None:\n            target_channels = np.array([ch for ch in range(mask.shape[-1]) if ch not in self.ignore_channels])\n            mask = np.take(mask, target_channels, axis=-1)\n\n        if self.height > mask_height or self.width > mask_width:\n            raise ValueError(\n                f\"Crop size ({self.height},{self.width}) is larger than image ({mask_height},{mask_width})\",\n            )\n\n        return mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        \"\"\"Get crop coordinates based on mask content.\"\"\"\n        if \"mask\" in data:\n            mask = self._preprocess_mask(data[\"mask\"])\n        elif \"masks\" in data and len(data[\"masks\"]):\n            masks = data[\"masks\"]\n            mask = self._preprocess_mask(np.copy(masks[0]))\n            for m in masks[1:]:\n                mask |= self._preprocess_mask(m)\n        else:\n            msg = \"Can not find mask for CropNonEmptyMaskIfExists\"\n            raise RuntimeError(msg)\n\n        mask_height, mask_width = mask.shape[:2]\n\n        if mask.any():\n            # Find non-zero regions in mask\n            mask_sum = mask.sum(axis=-1) if mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS else mask\n            non_zero_yx = np.argwhere(mask_sum)\n            y, x = self.py_random.choice(non_zero_yx)\n\n            # Calculate crop coordinates centered around chosen point\n            x_min = x - self.py_random.randint(0, self.width - 1)\n            y_min = y - self.py_random.randint(0, self.height - 1)\n            x_min = np.clip(x_min, 0, mask_width - self.width)\n            y_min = np.clip(y_min, 0, mask_height - self.height)\n        else:\n            # Random crop if no non-zero regions\n            x_min = self.py_random.randint(0, mask_width - self.width)\n            y_min = self.py_random.randint(0, mask_height - self.height)\n\n        x_max = x_min + self.width\n        y_max = y_min + self.height\n\n        return {\"crop_coords\": (x_min, y_min, x_max, y_max)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"ignore_values\", \"ignore_channels\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCrop","title":"class RandomCrop (height, width, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0.0, fill_mask=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input.

Parameters:

Name Type Description height int

height of the crop.

width int

width of the crop.

pad_if_needed bool

Whether to pad if crop size exceeds image size. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value for images if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks if border_mode is cv2.BORDER_CONSTANT. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded before applying the random crop.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCrop(BaseCropAndPad):\n    \"\"\"Crop a random part of the input.\n\n    Args:\n        height: height of the crop.\n        width: width of the crop.\n        pad_if_needed (bool): Whether to pad if crop size exceeds image size. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value for images if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p: probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded\n        before applying the random crop.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_dimensions(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n            return self\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0.0,\n        fill_mask: ColorType = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.height = height\n        self.width = width\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:  # Changed return type to be more flexible\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        if not self.pad_if_needed and (self.height > image_height or self.width > image_width):\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_shape[:2]}\",\n            )\n\n        # Get padding params first if needed\n        pad_params = self._get_pad_params(image_shape, (self.height, self.width))\n\n        # If padding is needed, adjust the image shape for crop calculation\n        if pad_params is not None:\n            pad_top = pad_params[\"pad_top\"]\n            pad_bottom = pad_params[\"pad_bottom\"]\n            pad_left = pad_params[\"pad_left\"]\n            pad_right = pad_params[\"pad_right\"]\n\n            padded_height = image_height + pad_top + pad_bottom\n            padded_width = image_width + pad_left + pad_right\n            padded_shape = (padded_height, padded_width)\n\n            # Get random crop coordinates based on padded dimensions\n            h_start = self.py_random.random()\n            w_start = self.py_random.random()\n            crop_coords = fcrops.get_crop_coords(padded_shape, (self.height, self.width), h_start, w_start)\n        else:\n            # Get random crop coordinates based on original dimensions\n            h_start = self.py_random.random()\n            w_start = self.py_random.random()\n            crop_coords = fcrops.get_crop_coords(image_shape, (self.height, self.width), h_start, w_start)\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"height\",\n            \"width\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCropFromBorders","title":"class RandomCropFromBorders (crop_left=0.1, crop_right=0.1, crop_top=0.1, crop_bottom=0.1, always_apply=None, p=1.0) [view source on GitHub]","text":"

Randomly crops the input from its borders without resizing.

This transform randomly crops parts of the input (image, mask, bounding boxes, or keypoints) from each of its borders. The amount of cropping is specified as a fraction of the input's dimensions for each side independently.

Parameters:

Name Type Description crop_left float

The maximum fraction of width to crop from the left side. Must be in the range [0.0, 1.0]. Default: 0.1

crop_right float

The maximum fraction of width to crop from the right side. Must be in the range [0.0, 1.0]. Default: 0.1

crop_top float

The maximum fraction of height to crop from the top. Must be in the range [0.0, 1.0]. Default: 0.1

crop_bottom float

The maximum fraction of height to crop from the bottom. Must be in the range [0.0, 1.0]. Default: 0.1

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The actual amount of cropping for each side is randomly chosen between 0 and the specified maximum for each application of the transform.
  • The sum of crop_left and crop_right must not exceed 1.0, and the sum of crop_top and crop_bottom must not exceed 1.0. Otherwise, a ValueError will be raised.
  • This transform does not resize the input after cropping, so the output dimensions will be smaller than the input dimensions.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomCropFromBorders(\n...     crop_left=0.1, crop_right=0.2, crop_top=0.2, crop_bottom=0.1, p=1.0\n... )\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image will have random crops from each border, with the maximum\n# possible crops being 10% from the left, 20% from the right, 20% from the top,\n# and 10% from the bottom. The image size will be reduced accordingly.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCropFromBorders(BaseCrop):\n    \"\"\"Randomly crops the input from its borders without resizing.\n\n    This transform randomly crops parts of the input (image, mask, bounding boxes, or keypoints)\n    from each of its borders. The amount of cropping is specified as a fraction of the input's\n    dimensions for each side independently.\n\n    Args:\n        crop_left (float): The maximum fraction of width to crop from the left side.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_right (float): The maximum fraction of width to crop from the right side.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_top (float): The maximum fraction of height to crop from the top.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_bottom (float): The maximum fraction of height to crop from the bottom.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual amount of cropping for each side is randomly chosen between 0 and\n          the specified maximum for each application of the transform.\n        - The sum of crop_left and crop_right must not exceed 1.0, and the sum of\n          crop_top and crop_bottom must not exceed 1.0. Otherwise, a ValueError will be raised.\n        - This transform does not resize the input after cropping, so the output dimensions\n          will be smaller than the input dimensions.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomCropFromBorders(\n        ...     crop_left=0.1, crop_right=0.2, crop_top=0.2, crop_bottom=0.1, p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image will have random crops from each border, with the maximum\n        # possible crops being 10% from the left, 20% from the right, 20% from the top,\n        # and 10% from the bottom. The image size will be reduced accordingly.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        crop_left: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_right: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_top: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_bottom: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n\n        @model_validator(mode=\"after\")\n        def validate_crop_values(self) -> Self:\n            if self.crop_left + self.crop_right > 1.0:\n                msg = \"The sum of crop_left and crop_right must be <= 1.\"\n                raise ValueError(msg)\n            if self.crop_top + self.crop_bottom > 1.0:\n                msg = \"The sum of crop_top and crop_bottom must be <= 1.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        crop_left: float = 0.1,\n        crop_right: float = 0.1,\n        crop_top: float = 0.1,\n        crop_bottom: float = 0.1,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p)\n        self.crop_left = crop_left\n        self.crop_right = crop_right\n        self.crop_top = crop_top\n        self.crop_bottom = crop_bottom\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        height, width = params[\"shape\"][:2]\n\n        x_min = self.py_random.randint(0, int(self.crop_left * width))\n        x_max = self.py_random.randint(max(x_min + 1, int((1 - self.crop_right) * width)), width)\n\n        y_min = self.py_random.randint(0, int(self.crop_top * height))\n        y_max = self.py_random.randint(max(y_min + 1, int((1 - self.crop_bottom) * height)), height)\n\n        crop_coords = x_min, y_min, x_max, y_max\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"crop_left\", \"crop_right\", \"crop_top\", \"crop_bottom\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCropNearBBox","title":"class RandomCropNearBBox (max_part_shift=(0, 0.3), cropping_bbox_key='cropping_bbox', cropping_box_key=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

Crop bbox from image with random shift by x,y coordinates

Parameters:

Name Type Description max_part_shift float, (float, float

Max shift in height and width dimensions relative to cropping_bbox dimension. If max_part_shift is a single float, the range will be (0, max_part_shift). Default (0, 0.3).

cropping_bbox_key str

Additional target key for cropping box. Default cropping_bbox.

cropping_box_key str

[Deprecated] Use cropping_bbox_key instead.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> aug = Compose([RandomCropNearBBox(max_part_shift=(0.1, 0.5), cropping_bbox_key='test_bbox')],\n>>>              bbox_params=BboxParams(\"pascal_voc\"))\n>>> result = aug(image=image, bboxes=bboxes, test_bbox=[0, 5, 10, 20])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCropNearBBox(BaseCrop):\n    \"\"\"Crop bbox from image with random shift by x,y coordinates\n\n    Args:\n        max_part_shift (float, (float, float)): Max shift in `height` and `width` dimensions relative\n            to `cropping_bbox` dimension.\n            If max_part_shift is a single float, the range will be (0, max_part_shift).\n            Default (0, 0.3).\n        cropping_bbox_key (str): Additional target key for cropping box. Default `cropping_bbox`.\n        cropping_box_key (str): [Deprecated] Use `cropping_bbox_key` instead.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Examples:\n        >>> aug = Compose([RandomCropNearBBox(max_part_shift=(0.1, 0.5), cropping_bbox_key='test_bbox')],\n        >>>              bbox_params=BboxParams(\"pascal_voc\"))\n        >>> result = aug(image=image, bboxes=bboxes, test_bbox=[0, 5, 10, 20])\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_part_shift: ZeroOneRangeType\n        cropping_bbox_key: str\n\n    def __init__(\n        self,\n        max_part_shift: ScaleFloatType = (0, 0.3),\n        cropping_bbox_key: str = \"cropping_bbox\",\n        cropping_box_key: str | None = None,  # Deprecated\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p)\n        # Check for deprecated parameter and issue warning\n        if cropping_box_key is not None:\n            warn(\n                \"The parameter 'cropping_box_key' is deprecated and will be removed in future versions. \"\n                \"Use 'cropping_bbox_key' instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            # Ensure the new parameter is used even if the old one is passed\n            cropping_bbox_key = cropping_box_key\n\n        self.max_part_shift = cast(tuple[float, float], max_part_shift)\n        self.cropping_bbox_key = cropping_bbox_key\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[float, ...]]:\n        bbox = data[self.cropping_bbox_key]\n\n        image_shape = params[\"shape\"][:2]\n\n        bbox = self._clip_bbox(bbox, image_shape)\n\n        h_max_shift = round((bbox[3] - bbox[1]) * self.max_part_shift[0])\n        w_max_shift = round((bbox[2] - bbox[0]) * self.max_part_shift[1])\n\n        x_min = bbox[0] - self.py_random.randint(-w_max_shift, w_max_shift)\n        x_max = bbox[2] + self.py_random.randint(-w_max_shift, w_max_shift)\n\n        y_min = bbox[1] - self.py_random.randint(-h_max_shift, h_max_shift)\n        y_max = bbox[3] + self.py_random.randint(-h_max_shift, h_max_shift)\n\n        crop_coords = self._clip_bbox((x_min, y_min, x_max, y_max), image_shape)\n\n        if crop_coords[0] == crop_coords[2] or crop_coords[1] == crop_coords[3]:\n            crop_shape = (bbox[3] - bbox[1], bbox[2] - bbox[0])\n            crop_coords = fcrops.get_center_crop_coords(image_shape, crop_shape)\n\n        return {\"crop_coords\": crop_coords}\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [self.cropping_bbox_key]\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_part_shift\", \"cropping_bbox_key\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomResizedCrop","title":"class RandomResizedCrop (size=None, width=None, height=None, *, scale=(0.08, 1.0), ratio=(0.75, 1.3333333333333333), interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specified size.

This transform first crops a random portion of the input image (or mask, bounding boxes, keypoints) and then resizes the crop to a specified size. It's particularly useful for training neural networks on images of varying sizes and aspect ratios.

Parameters:

Name Type Description size tuple[int, int]

Target size for the output image, i.e. (height, width) after crop and resize.

scale tuple[float, float]

Range of the random size of the crop relative to the input size. For example, (0.08, 1.0) means the crop size will be between 8% and 100% of the input size. Default: (0.08, 1.0)

ratio tuple[float, float]

Range of aspect ratios of the random crop. For example, (0.75, 1.3333) allows crop aspect ratios from 3:4 to 4:3. Default: (0.75, 1.3333333333333333)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform attempts to crop a random area with an aspect ratio and relative size specified by 'ratio' and 'scale' parameters. If it fails to find a suitable crop after 10 attempts, it will return a crop from the center of the image.
  • The crop's aspect ratio is defined as width / height.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.
  • After cropping, the result is resized to the specified size.

Mathematical Details: 1. A target area A is sampled from the range [scale[0] * input_area, scale[1] * input_area]. 2. A target aspect ratio r is sampled from the range [ratio[0], ratio[1]]. 3. The crop width and height are computed as: w = sqrt(A * r) h = sqrt(A / r) 4. If w and h are within the input image dimensions, the crop is accepted. Otherwise, steps 1-3 are repeated (up to 10 times). 5. If no valid crop is found after 10 attempts, a centered crop is taken. 6. The crop is then resized to the specified size.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomResizedCrop(size=80, scale=(0.5, 1.0), ratio=(0.75, 1.33), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# transformed_image will be a 80x80 crop from a random location in the original image,\n# with the crop's size between 50% and 100% of the original image size,\n# and the crop's aspect ratio between 3:4 and 4:3.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomResizedCrop(_BaseRandomSizedCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specified size.\n\n    This transform first crops a random portion of the input image (or mask, bounding boxes, keypoints)\n    and then resizes the crop to a specified size. It's particularly useful for training neural networks\n    on images of varying sizes and aspect ratios.\n\n    Args:\n        size (tuple[int, int]): Target size for the output image, i.e. (height, width) after crop and resize.\n        scale (tuple[float, float]): Range of the random size of the crop relative to the input size.\n            For example, (0.08, 1.0) means the crop size will be between 8% and 100% of the input size.\n            Default: (0.08, 1.0)\n        ratio (tuple[float, float]): Range of aspect ratios of the random crop.\n            For example, (0.75, 1.3333) allows crop aspect ratios from 3:4 to 4:3.\n            Default: (0.75, 1.3333333333333333)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform attempts to crop a random area with an aspect ratio and relative size\n          specified by 'ratio' and 'scale' parameters. If it fails to find a suitable crop after\n          10 attempts, it will return a crop from the center of the image.\n        - The crop's aspect ratio is defined as width / height.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n        - After cropping, the result is resized to the specified size.\n\n    Mathematical Details:\n        1. A target area A is sampled from the range [scale[0] * input_area, scale[1] * input_area].\n        2. A target aspect ratio r is sampled from the range [ratio[0], ratio[1]].\n        3. The crop width and height are computed as:\n           w = sqrt(A * r)\n           h = sqrt(A / r)\n        4. If w and h are within the input image dimensions, the crop is accepted.\n           Otherwise, steps 1-3 are repeated (up to 10 times).\n        5. If no valid crop is found after 10 attempts, a centered crop is taken.\n        6. The crop is then resized to the specified size.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomResizedCrop(size=80, scale=(0.5, 1.0), ratio=(0.75, 1.33), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # transformed_image will be a 80x80 crop from a random location in the original image,\n        # with the crop's size between 50% and 100% of the original image size,\n        # and the crop's aspect ratio between 3:4 and 4:3.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1)), AfterValidator(nondecreasing)]\n        ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        width: int | None\n        height: int | None\n        size: ScaleIntType | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def process(self) -> Self:\n            if isinstance(self.size, int):\n                if isinstance(self.width, int):\n                    warn(\n                        \"Initializing with 'size' as an integer and a separate 'width', `height` are deprecated. \"\n                        \"Please use a tuple (height, width) for the 'size' argument.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                    self.size = (self.size, self.width)\n                else:\n                    msg = \"If size is an integer, width as integer must be specified.\"\n                    raise TypeError(msg)\n\n            if self.size is None:\n                if self.height is None or self.width is None:\n                    message = \"If 'size' is not provided, both 'height' and 'width' must be specified.\"\n                    raise ValueError(message)\n                self.size = (self.height, self.width)\n\n            return self\n\n    def __init__(\n        self,\n        # NOTE @zetyquickly: when (width, height) are deprecated, make 'size' non optional\n        size: ScaleIntType | None = None,\n        width: int | None = None,\n        height: int | None = None,\n        *,\n        scale: tuple[float, float] = (0.08, 1.0),\n        ratio: tuple[float, float] = (0.75, 1.3333333333333333),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            size=cast(tuple[int, int], size),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale = scale\n        self.ratio = ratio\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        area = image_height * image_width\n\n        for _ in range(10):\n            target_area = self.py_random.uniform(*self.scale) * area\n            log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1]))\n            aspect_ratio = math.exp(self.py_random.uniform(*log_ratio))\n\n            width = int(round(math.sqrt(target_area * aspect_ratio)))\n            height = int(round(math.sqrt(target_area / aspect_ratio)))\n\n            if 0 < width <= image_width and 0 < height <= image_height:\n                i = self.py_random.randint(0, image_height - height)\n                j = self.py_random.randint(0, image_width - width)\n\n                h_start = i * 1.0 / (image_height - height + 1e-10)\n                w_start = j * 1.0 / (image_width - width + 1e-10)\n\n                crop_shape = (height, width)\n\n                crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n                return {\"crop_coords\": crop_coords}\n\n        # Fallback to central crop\n        in_ratio = image_width / image_height\n        if in_ratio < min(self.ratio):\n            width = image_width\n            height = int(round(image_width / min(self.ratio)))\n        elif in_ratio > max(self.ratio):\n            height = image_height\n            width = int(round(height * max(self.ratio)))\n        else:  # whole image\n            width = image_width\n            height = image_height\n\n        i = (image_height - height) // 2\n        j = (image_width - width) // 2\n\n        h_start = i * 1.0 / (image_height - height + 1e-10)\n        w_start = j * 1.0 / (image_width - width + 1e-10)\n\n        crop_shape = (height, width)\n\n        crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"scale\", \"ratio\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomSizedBBoxSafeCrop","title":"class RandomSizedBBoxSafeCrop (height, width, erosion_rate=0.0, interpolation=1, mask_interpolation=0, always_apply=None, p=1.0) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specific size without loss of bounding boxes.

This transform first attempts to crop a random portion of the input image while ensuring that all bounding boxes remain within the cropped area. It then resizes the crop to the specified size. This is particularly useful for object detection tasks where preserving all objects in the image is crucial while also standardizing the image size.

Parameters:

Name Type Description height int

Height of the output image after resizing.

width int

Width of the output image after resizing.

erosion_rate float

A value between 0.0 and 1.0 that determines the minimum allowable size of the crop as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be at least 80% of the original image height and width. Default: 0.0 (no minimum size).

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform ensures that all bounding boxes in the original image are fully contained within the cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out), it will default to cropping the entire image.
  • After cropping, the result is resized to the specified (height, width) size.
  • Bounding box coordinates are adjusted to match the new image size.
  • Keypoints are moved along with the crop and scaled to the new image size.
  • If there are no bounding boxes in the image, it will fall back to a random crop.

Mathematical Details: 1. A crop region is selected that includes all bounding boxes. 2. The crop size is determined by the erosion_rate: min_crop_size = (1 - erosion_rate) * original_size 3. If the selected crop is smaller than min_crop_size, it's expanded to meet this requirement. 4. The crop is then resized to the specified (height, width) size. 5. Bounding box coordinates are transformed to match the new image size: new_coord = (old_coord - crop_start) * (new_size / crop_size)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (300, 300, 3), dtype=np.uint8)\n>>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n>>> transform = A.Compose([\n...     A.RandomSizedBBoxSafeCrop(height=224, width=224, erosion_rate=0.2, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n>>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n>>> transformed_image = transformed['image']\n>>> transformed_bboxes = transformed['bboxes']\n# transformed_image will be a 224x224 image containing all original bounding boxes,\n# with their coordinates adjusted to the new image size.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomSizedBBoxSafeCrop(BBoxSafeRandomCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specific size without loss of bounding boxes.\n\n    This transform first attempts to crop a random portion of the input image while ensuring that all bounding boxes\n    remain within the cropped area. It then resizes the crop to the specified size. This is particularly useful for\n    object detection tasks where preserving all objects in the image is crucial while also standardizing the image size.\n\n    Args:\n        height (int): Height of the output image after resizing.\n        width (int): Width of the output image after resizing.\n        erosion_rate (float): A value between 0.0 and 1.0 that determines the minimum allowable size of the crop\n            as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be\n            at least 80% of the original image height and width. Default: 0.0 (no minimum size).\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform ensures that all bounding boxes in the original image are fully contained within the\n          cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out),\n          it will default to cropping the entire image.\n        - After cropping, the result is resized to the specified (height, width) size.\n        - Bounding box coordinates are adjusted to match the new image size.\n        - Keypoints are moved along with the crop and scaled to the new image size.\n        - If there are no bounding boxes in the image, it will fall back to a random crop.\n\n    Mathematical Details:\n        1. A crop region is selected that includes all bounding boxes.\n        2. The crop size is determined by the erosion_rate:\n           min_crop_size = (1 - erosion_rate) * original_size\n        3. If the selected crop is smaller than min_crop_size, it's expanded to meet this requirement.\n        4. The crop is then resized to the specified (height, width) size.\n        5. Bounding box coordinates are transformed to match the new image size:\n           new_coord = (old_coord - crop_start) * (new_size / crop_size)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (300, 300, 3), dtype=np.uint8)\n        >>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n        >>> transform = A.Compose([\n        ...     A.RandomSizedBBoxSafeCrop(height=224, width=224, erosion_rate=0.2, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n        >>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n        >>> transformed_image = transformed['image']\n        >>> transformed_bboxes = transformed['bboxes']\n        # transformed_image will be a 224x224 image containing all original bounding boxes,\n        # with their coordinates adjusted to the new image size.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        erosion_rate: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        erosion_rate: float = 0.0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(erosion_rate=erosion_rate, p=p)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        crop = fcrops.crop(img, *crop_coords)\n        return fgeometric.resize(crop, (self.height, self.width), self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        crop = fcrops.crop(mask, *crop_coords)\n        return fgeometric.resize(crop, (self.height, self.width), self.mask_interpolation)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints = fcrops.crop_keypoints_by_coords(keypoints, crop_coords)\n\n        crop_height = crop_coords[3] - crop_coords[1]\n        crop_width = crop_coords[2] - crop_coords[0]\n\n        scale_y = self.height / crop_height\n        scale_x = self.width / crop_width\n        return fgeometric.keypoints_scale(keypoints, scale_x=scale_x, scale_y=scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"height\", \"width\", \"interpolation\", \"mask_interpolation\")\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomSizedCrop","title":"class RandomSizedCrop (min_max_height, size=None, width=None, height=None, *, w2h_ratio=1.0, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specific size.

This transform first crops a random portion of the input and then resizes it to a specified size. The size of the random crop is controlled by the 'min_max_height' parameter.

Parameters:

Name Type Description min_max_height tuple[int, int]

Minimum and maximum height of the crop in pixels.

size tuple[int, int]

Target size for the output image, i.e. (height, width) after crop and resize.

w2h_ratio float

Aspect ratio (width/height) of crop. Default: 1.0

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The crop size is randomly selected for each execution within the range specified by 'min_max_height'.
  • The aspect ratio of the crop is determined by the 'w2h_ratio' parameter.
  • After cropping, the result is resized to the specified 'size'.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.
  • This transform differs from RandomResizedCrop in that it allows more control over the crop size through the 'min_max_height' parameter, rather than using a scale parameter.

Mathematical Details: 1. A random crop height h is sampled from the range [min_max_height[0], min_max_height[1]]. 2. The crop width w is calculated as: w = h * w2h_ratio 3. A random location for the crop is selected within the input image. 4. The image is cropped to the size (h, w). 5. The crop is then resized to the specified 'size'.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomSizedCrop(\n...     min_max_height=(50, 80),\n...     size=(64, 64),\n...     w2h_ratio=1.0,\n...     interpolation=cv2.INTER_LINEAR,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# transformed_image will be a 64x64 image, resulting from a crop with height\n# between 50 and 80 pixels, and the same aspect ratio as specified by w2h_ratio,\n# taken from a random location in the original image and then resized.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomSizedCrop(_BaseRandomSizedCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specific size.\n\n    This transform first crops a random portion of the input and then resizes it to a specified size.\n    The size of the random crop is controlled by the 'min_max_height' parameter.\n\n    Args:\n        min_max_height (tuple[int, int]): Minimum and maximum height of the crop in pixels.\n        size (tuple[int, int]): Target size for the output image, i.e. (height, width) after crop and resize.\n        w2h_ratio (float): Aspect ratio (width/height) of crop. Default: 1.0\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The crop size is randomly selected for each execution within the range specified by 'min_max_height'.\n        - The aspect ratio of the crop is determined by the 'w2h_ratio' parameter.\n        - After cropping, the result is resized to the specified 'size'.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n        - This transform differs from RandomResizedCrop in that it allows more control over the crop size\n          through the 'min_max_height' parameter, rather than using a scale parameter.\n\n    Mathematical Details:\n        1. A random crop height h is sampled from the range [min_max_height[0], min_max_height[1]].\n        2. The crop width w is calculated as: w = h * w2h_ratio\n        3. A random location for the crop is selected within the input image.\n        4. The image is cropped to the size (h, w).\n        5. The crop is then resized to the specified 'size'.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomSizedCrop(\n        ...     min_max_height=(50, 80),\n        ...     size=(64, 64),\n        ...     w2h_ratio=1.0,\n        ...     interpolation=cv2.INTER_LINEAR,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # transformed_image will be a 64x64 image, resulting from a crop with height\n        # between 50 and 80 pixels, and the same aspect ratio as specified by w2h_ratio,\n        # taken from a random location in the original image and then resized.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        min_max_height: OnePlusIntRangeType\n        w2h_ratio: Annotated[float, Field(gt=0)]\n        width: int | None\n        height: int | None\n        size: ScaleIntType | None\n\n        @model_validator(mode=\"after\")\n        def process(self) -> Self:\n            if isinstance(self.size, int):\n                if isinstance(self.width, int):\n                    warn(\n                        \"Initializing with 'size' as an integer and a separate 'width', `height` are deprecated. \"\n                        \"Please use a tuple (height, width) for the 'size' argument.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                    self.size = (self.size, self.width)\n                else:\n                    msg = \"If size is an integer, width as integer must be specified.\"\n                    raise TypeError(msg)\n\n            if self.size is None:\n                if self.height is None or self.width is None:\n                    message = \"If 'size' is not provided, both 'height' and 'width' must be specified.\"\n                    raise ValueError(message)\n                self.size = (self.height, self.width)\n            return self\n\n    def __init__(\n        self,\n        min_max_height: tuple[int, int],\n        # NOTE @zetyquickly: when (width, height) are deprecated, make 'size' non optional\n        size: ScaleIntType | None = None,\n        width: int | None = None,\n        height: int | None = None,\n        *,\n        w2h_ratio: float = 1.0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            size=cast(tuple[int, int], size),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.min_max_height = min_max_height\n        self.w2h_ratio = w2h_ratio\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n\n        crop_height = self.py_random.randint(*self.min_max_height)\n        crop_width = int(crop_height * self.w2h_ratio)\n\n        crop_shape = (crop_height, crop_width)\n\n        h_start = self.py_random.random()\n        w_start = self.py_random.random()\n\n        crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"min_max_height\", \"w2h_ratio\")\n
"},{"location":"api_reference/augmentations/domain_adaptation/","title":"Index","text":"
  • Domain Adaptation functional transforms (albumentations.augmentations.domain_adaptation.functional)
  • Domain Adaptation transforms (albumentations.augmentations.domain_adaptation.transforms)
"},{"location":"api_reference/augmentations/domain_adaptation/functional/","title":"Domain Adaptation functional transforms (augmentations.domain_adaptation.functional)","text":""},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.apply_histogram","title":"def apply_histogram (img, reference_image, blend_ratio) [view source on GitHub]","text":"

Apply histogram matching to an input image using a reference image and blend the result.

This function performs histogram matching between the input image and a reference image, then blends the result with the original input image based on the specified blend ratio.

Parameters:

Name Type Description img np.ndarray

The input image to be transformed. Can be either grayscale or RGB. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

reference_image np.ndarray

The reference image used for histogram matching. Should have the same number of channels as the input image. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

blend_ratio float

The ratio for blending the matched image with the original image. Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.

Returns:

Type Description np.ndarray

The transformed image after histogram matching and blending. The output will have the same shape and dtype as the input image.

Supported image types: - Grayscale images: 2D arrays - RGB images: 3D arrays with 3 channels - Multispectral images: 3D arrays with more than 3 channels

Note

  • If the input and reference images have different sizes, the reference image will be resized to match the input image's dimensions.
  • The function uses a custom implementation of histogram matching based on OpenCV and NumPy.
  • The @clipped and @preserve_channel_dim decorators ensure the output is within the valid range and maintains the original number of dimensions.
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef apply_histogram(img: np.ndarray, reference_image: np.ndarray, blend_ratio: float) -> np.ndarray:\n    \"\"\"Apply histogram matching to an input image using a reference image and blend the result.\n\n    This function performs histogram matching between the input image and a reference image,\n    then blends the result with the original input image based on the specified blend ratio.\n\n    Args:\n        img (np.ndarray): The input image to be transformed. Can be either grayscale or RGB.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        reference_image (np.ndarray): The reference image used for histogram matching.\n            Should have the same number of channels as the input image.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        blend_ratio (float): The ratio for blending the matched image with the original image.\n            Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.\n\n    Returns:\n        np.ndarray: The transformed image after histogram matching and blending.\n            The output will have the same shape and dtype as the input image.\n\n    Supported image types:\n        - Grayscale images: 2D arrays\n        - RGB images: 3D arrays with 3 channels\n        - Multispectral images: 3D arrays with more than 3 channels\n\n    Note:\n        - If the input and reference images have different sizes, the reference image\n          will be resized to match the input image's dimensions.\n        - The function uses a custom implementation of histogram matching based on OpenCV and NumPy.\n        - The @clipped and @preserve_channel_dim decorators ensure the output is within\n          the valid range and maintains the original number of dimensions.\n    \"\"\"\n    # Resize reference image only if necessary\n    if img.shape[:2] != reference_image.shape[:2]:\n        reference_image = cv2.resize(reference_image, dsize=(img.shape[1], img.shape[0]))\n\n    img = np.squeeze(img)\n    reference_image = np.squeeze(reference_image)\n\n    # Match histograms between the images\n    matched = match_histograms(img, reference_image)\n\n    # Blend the original image and the matched image\n    return add_weighted(matched, blend_ratio, img, 1 - blend_ratio)\n
"},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.fourier_domain_adaptation","title":"def fourier_domain_adaptation (img, target_img, beta) [view source on GitHub]","text":"

Apply Fourier Domain Adaptation to the input image using a target image.

This function performs domain adaptation in the frequency domain by modifying the amplitude spectrum of the source image based on the target image's amplitude spectrum. It preserves the phase information of the source image, which helps maintain its content while adapting its style to match the target image.

Parameters:

Name Type Description img np.ndarray

The source image to be adapted. Can be grayscale or RGB.

target_img np.ndarray

The target image used as a reference for adaptation. Should have the same dimensions as the source image.

beta float

The adaptation strength, typically in the range [0, 1]. Higher values result in stronger adaptation towards the target image's style.

Returns:

Type Description np.ndarray

The adapted image with the same shape and type as the input image.

Exceptions:

Type Description ValueError

If the source and target images have different shapes.

Note

  • Both input images are converted to float32 for processing.
  • The function handles both grayscale (2D) and color (3D) images.
  • For grayscale images, an extra dimension is added to facilitate uniform processing.
  • The adaptation is performed channel-wise for color images.
  • The output is clipped to the valid range and preserves the original number of channels.

The adaptation process involves the following steps for each channel: 1. Compute the 2D Fourier Transform of both source and target images. 2. Shift the zero frequency component to the center of the spectrum. 3. Extract amplitude and phase information from the source image's spectrum. 4. Mutate the source amplitude using the target amplitude and the beta parameter. 5. Combine the mutated amplitude with the original phase. 6. Perform the inverse Fourier Transform to obtain the adapted channel.

The low_freq_mutate function (not shown here) is responsible for the actual amplitude mutation, focusing on low-frequency components which carry style information.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n>>> assert adapted_img.shape == source_img.shape\n

References

  • \"FDA: Fourier Domain Adaptation for Semantic Segmentation\" (Yang and Soatto, 2020, CVPR) https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef fourier_domain_adaptation(img: np.ndarray, target_img: np.ndarray, beta: float) -> np.ndarray:\n    \"\"\"Apply Fourier Domain Adaptation to the input image using a target image.\n\n    This function performs domain adaptation in the frequency domain by modifying the amplitude\n    spectrum of the source image based on the target image's amplitude spectrum. It preserves\n    the phase information of the source image, which helps maintain its content while adapting\n    its style to match the target image.\n\n    Args:\n        img (np.ndarray): The source image to be adapted. Can be grayscale or RGB.\n        target_img (np.ndarray): The target image used as a reference for adaptation.\n            Should have the same dimensions as the source image.\n        beta (float): The adaptation strength, typically in the range [0, 1].\n            Higher values result in stronger adaptation towards the target image's style.\n\n    Returns:\n        np.ndarray: The adapted image with the same shape and type as the input image.\n\n    Raises:\n        ValueError: If the source and target images have different shapes.\n\n    Note:\n        - Both input images are converted to float32 for processing.\n        - The function handles both grayscale (2D) and color (3D) images.\n        - For grayscale images, an extra dimension is added to facilitate uniform processing.\n        - The adaptation is performed channel-wise for color images.\n        - The output is clipped to the valid range and preserves the original number of channels.\n\n    The adaptation process involves the following steps for each channel:\n    1. Compute the 2D Fourier Transform of both source and target images.\n    2. Shift the zero frequency component to the center of the spectrum.\n    3. Extract amplitude and phase information from the source image's spectrum.\n    4. Mutate the source amplitude using the target amplitude and the beta parameter.\n    5. Combine the mutated amplitude with the original phase.\n    6. Perform the inverse Fourier Transform to obtain the adapted channel.\n\n    The `low_freq_mutate` function (not shown here) is responsible for the actual\n    amplitude mutation, focusing on low-frequency components which carry style information.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n        >>> assert adapted_img.shape == source_img.shape\n\n    References:\n        - \"FDA: Fourier Domain Adaptation for Semantic Segmentation\"\n          (Yang and Soatto, 2020, CVPR)\n          https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n    \"\"\"\n    src_img = img.astype(np.float32)\n    trg_img = target_img.astype(np.float32)\n\n    if src_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        src_img = np.expand_dims(src_img, axis=-1)\n    if trg_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        trg_img = np.expand_dims(trg_img, axis=-1)\n\n    num_channels = src_img.shape[-1]\n\n    # Prepare container for the output image\n    src_in_trg = np.zeros_like(src_img)\n\n    for channel_id in range(num_channels):\n        # Perform FFT on each channel\n        fft_src = np.fft.fft2(src_img[:, :, channel_id])\n        fft_trg = np.fft.fft2(trg_img[:, :, channel_id])\n\n        # Shift the zero frequency component to the center\n        fft_src_shifted = np.fft.fftshift(fft_src)\n        fft_trg_shifted = np.fft.fftshift(fft_trg)\n\n        # Extract amplitude and phase\n        amp_src, pha_src = np.abs(fft_src_shifted), np.angle(fft_src_shifted)\n        amp_trg = np.abs(fft_trg_shifted)\n\n        # Mutate the amplitude part of the source with the target\n        mutated_amp = low_freq_mutate(amp_src.copy(), amp_trg, beta)\n\n        # Combine the mutated amplitude with the original phase\n        fft_src_mutated = np.fft.ifftshift(mutated_amp * np.exp(1j * pha_src))\n\n        # Perform inverse FFT\n        src_in_trg_channel = np.fft.ifft2(fft_src_mutated)\n\n        # Store the result in the corresponding channel of the output image\n        src_in_trg[:, :, channel_id] = np.real(src_in_trg_channel)\n\n    return src_in_trg\n
"},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.match_histograms","title":"def match_histograms (image, reference) [view source on GitHub]","text":"

Adjust an image so that its cumulative histogram matches that of another.

The adjustment is applied separately for each channel.

Parameters:

Name Type Description image np.ndarray

Input image. Can be gray-scale or in color.

reference np.ndarray

Image to match histogram of. Must have the same number of channels as image.

channel_axis

If None, the image is assumed to be a grayscale (single channel) image. Otherwise, this parameter indicates which axis of the array corresponds to channels.

Returns:

Type Description np.ndarray

Transformed input image.

Exceptions:

Type Description ValueError

Thrown when the number of channels in the input image and the reference differ.

Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef match_histograms(image: np.ndarray, reference: np.ndarray) -> np.ndarray:\n    \"\"\"Adjust an image so that its cumulative histogram matches that of another.\n\n    The adjustment is applied separately for each channel.\n\n    Args:\n        image: Input image. Can be gray-scale or in color.\n        reference: Image to match histogram of. Must have the same number of channels as image.\n        channel_axis: If None, the image is assumed to be a grayscale (single channel) image.\n            Otherwise, this parameter indicates which axis of the array corresponds to channels.\n\n    Returns:\n        np.ndarray: Transformed input image.\n\n    Raises:\n        ValueError: Thrown when the number of channels in the input image and the reference differ.\n    \"\"\"\n    if reference.dtype != np.uint8:\n        reference = from_float(reference, np.uint8)\n\n    if image.ndim != reference.ndim:\n        raise ValueError(\"Image and reference must have the same number of dimensions.\")\n\n    # Expand dimensions for grayscale images\n    if image.ndim == 2:\n        image = np.expand_dims(image, axis=-1)\n    if reference.ndim == 2:\n        reference = np.expand_dims(reference, axis=-1)\n\n    matched = np.empty(image.shape, dtype=np.uint8)\n\n    num_channels = image.shape[-1]\n\n    for channel in range(num_channels):\n        matched_channel = _match_cumulative_cdf(image[..., channel], reference[..., channel]).astype(np.uint8)\n        matched[..., channel] = matched_channel\n\n    return matched\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/","title":"Domain Adaptation transforms (augmentations.domain_adaptation.transforms)","text":""},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.FDA","title":"class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Reference

  • https://github.com/YanchaoYang/FDA
  • https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n>>> result = aug(image=image)\n

Note

FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target domain samples are unavailable. It enables significant improvements in model generalization by aligning the low-level statistics of source and target images through a simple yet effective Fourier-based method.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class FDA(ImageOnlyTransform):\n    \"\"\"Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation\n    (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source\n    and target datasets, effectively adapting images from one domain to closely resemble those from another without\n    altering their semantic content.\n\n    This transform is particularly beneficial in scenarios where the training (source) and testing (target) images\n    come from different distributions, such as synthetic versus real images, or day versus night scenes.\n    Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain\n    alignment by swapping low-frequency components of the Fourier transform between the source and target images.\n    This technique has shown to improve the performance of models on the target domain, particularly for tasks\n    like semantic segmentation, without additional training for domain invariance.\n\n    The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more\n    of the original image's characteristics and higher values leading to more pronounced adaptation effects.\n    It is recommended to use beta values less than 0.3 to avoid introducing artifacts.\n\n    Args:\n        reference_images (Sequence[Any]): Sequence of objects to be converted into images by `read_fn`. This typically\n            involves paths to images that serve as target domain examples for adaptation.\n        beta_limit (tuple[float, float] | float): Coefficient beta from the paper, controlling the swapping extent of\n            frequency components. If one value is provided beta will be sampled from uniform\n            distribution [0, beta_limit]. Values should be less than 0.5.\n        read_fn (Callable): User-defined function for reading images. It takes an element from `reference_images` and\n            returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a\n            numpy array.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        - https://github.com/YanchaoYang/FDA\n        - https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n        >>> result = aug(image=image)\n\n    Note:\n        FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target\n        domain samples are unavailable. It enables significant improvements in model generalization by aligning\n        the low-level statistics of source and target images through a simple yet effective Fourier-based method.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        read_fn: Callable[[Any], np.ndarray]\n        beta_limit: ZeroOneRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_ranges(cls, value: tuple[float, float]) -> tuple[float, float]:\n            bounds = 0, MAX_BETA_LIMIT\n            if not bounds[0] <= value[0] <= value[1] <= bounds[1]:\n                raise ValueError(f\"Values should be in the range {bounds} got {value} \")\n            return value\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        beta_limit: ScaleFloatType = (0, 0.1),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        target_image: np.ndarray,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fourier_domain_adaptation(img, target_image, beta)\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n        target_img = self.read_fn(self.py_random.choice(self.reference_images))\n        target_img = cv2.resize(target_img, dsize=(width, height))\n\n        return {\"target_image\": target_img, \"beta\": self.py_random.uniform(*self.beta_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"reference_images\", \"beta_limit\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"FDA can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.HistogramMatching","title":"class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.HistogramMatching(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     read_fn=lambda x: x,\n...     p=1\n... )\n>>> result = transform(image=image)\n>>> matched_image = result[\"image\"]\n

References

  • Histogram Matching in scikit-image: https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class HistogramMatching(ImageOnlyTransform):\n    \"\"\"Adjust the pixel values of an input image to match the histogram of a reference image.\n\n    This transform applies histogram matching, a technique that modifies the distribution of pixel\n    intensities in the input image to closely resemble that of a reference image. This process is\n    performed independently for each channel in multi-channel images, provided both the input and\n    reference images have the same number of channels.\n\n    Histogram matching is particularly useful for:\n    - Normalizing images from different sources or captured under varying conditions.\n    - Preparing images for feature matching or other computer vision tasks where consistent\n      tone and contrast are important.\n    - Simulating different lighting or camera conditions in a controlled manner.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of reference image sources. These can be\n            file paths, URLs, or any objects that can be converted to images by the `read_fn`.\n        blend_ratio (tuple[float, float]): Range for the blending factor between the original\n            and the matched image. Must be two floats between 0 and 1, where:\n            - 0 means no blending (original image is returned)\n            - 1 means full histogram matching\n            A random value within this range is chosen for each application.\n            Default: (0.5, 1.0)\n        read_fn (Callable[[Any], np.ndarray]): A function that takes an element from\n            `reference_images` and returns a numpy array representing the image.\n            Default: read_rgb_image (reads image file from disk)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform cannot be directly serialized due to its dependency on external image data.\n        - The effectiveness of the matching depends on the similarity between the input and reference images.\n        - For best results, choose reference images that represent the desired tone and contrast.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.HistogramMatching(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     read_fn=lambda x: x,\n        ...     p=1\n        ... )\n        >>> result = transform(image=image)\n        >>> matched_image = result[\"image\"]\n\n    References:\n        - Histogram Matching in scikit-image:\n          https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.5, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n\n    def apply(\n        self: np.ndarray,\n        img: np.ndarray,\n        reference_image: np.ndarray,\n        blend_ratio: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return apply_histogram(img, reference_image, blend_ratio)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"HistogramMatching can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.PixelDistributionAdaptation","title":"class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7f9061366d40>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub]","text":"

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal[\"pca\", \"standard\", \"minmax\"]

Specifies the type of statistical transformation to apply. - \"pca\": Principal Component Analysis - \"standard\": StandardScaler (zero mean and unit variance) - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: \"pca\"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PixelDistributionAdaptation(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     transform_type=\"standard\",\n...     read_fn=lambda x: x,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> adapted_image = result[\"image\"]\n

References

  • https://github.com/arsenyinfo/qudida
  • https://arxiv.org/abs/1911.11483

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class PixelDistributionAdaptation(ImageOnlyTransform):\n    \"\"\"Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image\n    with that of a reference image. This process involves fitting a simple statistical transformation\n    (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images,\n    transforming the original image with the transformation trained on it, and then applying the inverse\n    transformation using the transform fitted on the reference image. The result is an adapted image\n    that retains the original content while mimicking the pixel value distribution of the reference domain.\n\n    The process can be visualized as two main steps:\n    1. Adjusting the original image to a standard distribution space using a selected transform.\n    2. Moving the adjusted image into the distribution space of the reference image by applying the inverse\n       of the transform fitted on the reference image.\n\n    This technique is especially useful in scenarios where images from different domains (e.g., synthetic\n    vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in\n    image processing tasks.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of objects (typically image paths) that will be\n            converted into images by `read_fn`. These images serve as references for the domain adaptation.\n        blend_ratio (tuple[float, float]): Specifies the minimum and maximum blend ratio for mixing\n            the adapted image with the original. This enhances the diversity of the output images.\n            Values should be in the range [0, 1]. Default: (0.25, 1.0)\n        read_fn (Callable): A user-defined function for reading and converting the objects in\n            `reference_images` into numpy arrays. By default, it assumes these objects are image paths.\n        transform_type (Literal[\"pca\", \"standard\", \"minmax\"]): Specifies the type of statistical\n            transformation to apply.\n            - \"pca\": Principal Component Analysis\n            - \"standard\": StandardScaler (zero mean and unit variance)\n            - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1])\n            Default: \"pca\"\n        p (float): The probability of applying the transform to any given image. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effectiveness of the adaptation depends on the similarity between the input and reference domains.\n        - PCA transformation may alter color relationships more significantly than other methods.\n        - StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.\n        - The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.\n        - This transform cannot be directly serialized due to its dependency on external image data.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PixelDistributionAdaptation(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     transform_type=\"standard\",\n        ...     read_fn=lambda x: x,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> adapted_image = result[\"image\"]\n\n    References:\n        - https://github.com/arsenyinfo/qudida\n        - https://arxiv.org/abs/1911.11483\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.25, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"] = \"pca\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n        self.transform_type = transform_type\n\n    def apply(self, img: np.ndarray, reference_image: np.ndarray, blend_ratio: float, **params: Any) -> np.ndarray:\n        return adapt_pixel_distribution(\n            img,\n            ref=reference_image,\n            weight=blend_ratio,\n            transform_type=self.transform_type,\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\", \"transform_type\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"PixelDistributionAdaptation can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.TemplateTransform","title":"class TemplateTransform (templates, img_weight=(0.5, 0.5), template_weight=None, template_transform=None, name=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply blending of input image with specified templates.

This transform overlays one or more template images onto the input image using alpha blending. It allows for creating complex composite images or simulating various visual effects.

Parameters:

Name Type Description templates numpy array | list[np.ndarray]

Images to use as templates for the transform. If a single numpy array is provided, it will be used as the only template. If a list of numpy arrays is provided, one will be randomly chosen for each application.

img_weight tuple[float, float] | float

Weight of the original image in the blend. If a single float, that value will always be used. If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application. To use a fixed weight, use (weight, weight). Default: (0.5, 0.5).

template_transform A.Compose | None

A composition of Albumentations transforms to apply to the template before blending. This should be an instance of A.Compose containing one or more Albumentations transforms. Default: None.

name str | None

Name of the transform instance. Used for serialization purposes. Default: None.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The template(s) must have the same number of channels as the input image or be single-channel.
  • If a single-channel template is used with a multi-channel image, the template will be replicated across all channels.
  • The template(s) will be resized to match the input image size if they differ.
  • To make this transform serializable, provide a name when initializing it.

Mathematical Formulation: Given: - I: Input image - T: Template image - w_i: Weight of input image (sampled from img_weight)

The blended image B is computed as:\n\nB = w_i * I + (1 - w_i) * T\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-a-single-template","title":"Apply template transform with a single template","text":"Python
>>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-multiple-templates-and-custom-weights","title":"Apply template transform with multiple templates and custom weights","text":"Python
>>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n>>> transform = A.TemplateTransform(\n...     templates=templates,\n...     img_weight=(0.3, 0.7),\n...     name=\"multi_template_transform\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-additional-transforms-on-the-template","title":"Apply template transform with additional transforms on the template","text":"Python
>>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n>>> transform = A.TemplateTransform(\n...     templates=template,\n...     img_weight=0.6,\n...     template_transform=template_transform,\n...     name=\"transformed_template\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Image blending: https://en.wikipedia.org/wiki/Image_blending

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class TemplateTransform(ImageOnlyTransform):\n    \"\"\"Apply blending of input image with specified templates.\n\n    This transform overlays one or more template images onto the input image using alpha blending.\n    It allows for creating complex composite images or simulating various visual effects.\n\n    Args:\n        templates (numpy array | list[np.ndarray]): Images to use as templates for the transform.\n            If a single numpy array is provided, it will be used as the only template.\n            If a list of numpy arrays is provided, one will be randomly chosen for each application.\n\n        img_weight (tuple[float, float]  | float): Weight of the original image in the blend.\n            If a single float, that value will always be used.\n            If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application.\n            To use a fixed weight, use (weight, weight).\n            Default: (0.5, 0.5).\n\n        template_transform (A.Compose | None): A composition of Albumentations transforms to apply to the template\n            before blending.\n            This should be an instance of A.Compose containing one or more Albumentations transforms.\n            Default: None.\n\n        name (str | None): Name of the transform instance. Used for serialization purposes.\n            Default: None.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The template(s) must have the same number of channels as the input image or be single-channel.\n        - If a single-channel template is used with a multi-channel image, the template will be replicated across\n          all channels.\n        - The template(s) will be resized to match the input image size if they differ.\n        - To make this transform serializable, provide a name when initializing it.\n\n    Mathematical Formulation:\n        Given:\n        - I: Input image\n        - T: Template image\n        - w_i: Weight of input image (sampled from img_weight)\n\n        The blended image B is computed as:\n\n        B = w_i * I + (1 - w_i) * T\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply template transform with a single template\n        >>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with multiple templates and custom weights\n        >>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n        >>> transform = A.TemplateTransform(\n        ...     templates=templates,\n        ...     img_weight=(0.3, 0.7),\n        ...     name=\"multi_template_transform\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with additional transforms on the template\n        >>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n        >>> transform = A.TemplateTransform(\n        ...     templates=template,\n        ...     img_weight=0.6,\n        ...     template_transform=template_transform,\n        ...     name=\"transformed_template\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Image blending: https://en.wikipedia.org/wiki/Image_blending\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        templates: np.ndarray | Sequence[np.ndarray]\n        img_weight: ZeroOneRangeType\n        template_weight: ZeroOneRangeType | None = Field(\n            deprecated=\"Template_weight is deprecated. Computed automatically as (1 - img_weight)\",\n        )\n        template_transform: Compose | BasicTransform | None = None\n        name: str | None\n\n        @field_validator(\"templates\")\n        @classmethod\n        def validate_templates(cls, v: np.ndarray | list[np.ndarray]) -> list[np.ndarray]:\n            if isinstance(v, np.ndarray):\n                return [v]\n            if isinstance(v, list):\n                if not all(isinstance(item, np.ndarray) for item in v):\n                    msg = \"All templates must be numpy arrays.\"\n                    raise ValueError(msg)\n                return v\n            msg = \"Templates must be a numpy array or a list of numpy arrays.\"\n            raise TypeError(msg)\n\n    def __init__(\n        self,\n        templates: np.ndarray | list[np.ndarray],\n        img_weight: ScaleFloatType = (0.5, 0.5),\n        template_weight: None = None,\n        template_transform: Compose | BasicTransform | None = None,\n        name: str | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.templates = templates\n        self.img_weight = cast(tuple[float, float], img_weight)\n        self.template_transform = template_transform\n        self.name = name\n\n    def apply(\n        self,\n        img: np.ndarray,\n        template: np.ndarray,\n        img_weight: float,\n        **params: Any,\n    ) -> np.ndarray:\n        if img_weight == 0:\n            return template\n        if img_weight == 1:\n            return img\n\n        return add_weighted(img, img_weight, template, 1 - img_weight)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"img_weight\": self.py_random.uniform(*self.img_weight),\n        }\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        template = self.py_random.choice(self.templates)\n\n        if self.template_transform is not None:\n            template = self.template_transform(image=template)[\"image\"]\n\n        if get_num_channels(template) not in [1, get_num_channels(image)]:\n            msg = (\n                \"Template must be a single channel or \"\n                \"has the same number of channels as input \"\n                f\"image ({get_num_channels(image)}), got {get_num_channels(template)}\"\n            )\n            raise ValueError(msg)\n\n        if template.dtype != image.dtype:\n            msg = \"Image and template must be the same image type\"\n            raise ValueError(msg)\n\n        if image.shape[:2] != template.shape[:2]:\n            template = fgeometric.resize(template, image.shape[:2], interpolation=cv2.INTER_AREA)\n\n        if get_num_channels(template) == 1 and get_num_channels(image) > 1:\n            # Replicate single channel template across all channels to match input image\n            template = cv2.merge([template] * get_num_channels(image))\n        # in order to support grayscale image with dummy dim\n        template = template.reshape(image.shape)\n\n        return {\"template\": template}\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a TemplateTransform serializable you should provide the `name` argument, \"\n                \"e.g. `TemplateTransform(name='my_transform', ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n
"},{"location":"api_reference/augmentations/dropout/","title":"Index","text":"
  • ChannelDropout augmentation (albumentations.augmentations.dropout.channel_dropout)
  • CoarseDropout augmentation (albumentations.augmentations.dropout.coarse_dropout)
  • GridDropout augmentation (albumentations.augmentations.dropout.grid_dropout)
  • MaskDropout augmentation (albumentations.augmentations.dropout.mask_dropout)
"},{"location":"api_reference/augmentations/dropout/channel_dropout/","title":"ChannelDropout augmentation (augmentations.dropout.channel_dropout)","text":""},{"location":"api_reference/augmentations/dropout/channel_dropout/#albumentations.augmentations.dropout.channel_dropout.ChannelDropout","title":"class ChannelDropout (channel_drop_range=(1, 1), fill_value=None, fill=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly drop channels in the input image.

This transform randomly selects a number of channels to drop from the input image and replaces them with a specified fill value. This can improve model robustness to missing or corrupted channels.

The technique is conceptually similar to: - Dropout layers in neural networks, which randomly set input units to 0 during training. - CoarseDropout augmentation, which drops out regions in the spatial dimensions of the image.

However, ChannelDropout operates on the channel dimension, effectively \"dropping out\" entire color channels or feature maps.

Parameters:

Name Type Description channel_drop_range tuple[int, int]

Range from which to choose the number of channels to drop. The actual number will be randomly selected from the inclusive range [min, max]. Default: (1, 1).

fill float

Pixel value used to fill the dropped channels. Default: 0.

p float

Probability of applying the transform. Must be in the range [0, 1]. Default: 0.5.

Exceptions:

Type Description NotImplementedError

If the input image has only one channel.

ValueError

If the upper bound of channel_drop_range is greater than or equal to the number of channels in the input image.

Targets

image, volume

Image types: uint8, float32

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ChannelDropout(channel_drop_range=(1, 2), fill=128, p=1.0)\n>>> result = transform(image=image)\n>>> dropped_image = result['image']\n>>> assert dropped_image.shape == image.shape\n>>> assert np.any(dropped_image != image)  # Some channels should be different\n

Note

  • The number of channels to drop is randomly chosen within the specified range.
  • Channels are randomly selected for dropping.
  • This transform is not applicable to single-channel (grayscale) images.
  • The transform will raise an error if it's not possible to drop the specified number of channels (e.g., trying to drop 3 channels from an RGB image).
  • This augmentation can be particularly useful for training models to be robust against missing or corrupted channel data in multi-spectral or hyperspectral imagery.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/channel_dropout.py Python
class ChannelDropout(ImageOnlyTransform):\n    \"\"\"Randomly drop channels in the input image.\n\n    This transform randomly selects a number of channels to drop from the input image\n    and replaces them with a specified fill value. This can improve model robustness\n    to missing or corrupted channels.\n\n    The technique is conceptually similar to:\n    - Dropout layers in neural networks, which randomly set input units to 0 during training.\n    - CoarseDropout augmentation, which drops out regions in the spatial dimensions of the image.\n\n    However, ChannelDropout operates on the channel dimension, effectively \"dropping out\"\n    entire color channels or feature maps.\n\n    Args:\n        channel_drop_range (tuple[int, int]): Range from which to choose the number\n            of channels to drop. The actual number will be randomly selected from\n            the inclusive range [min, max]. Default: (1, 1).\n        fill (float): Pixel value used to fill the dropped channels.\n            Default: 0.\n        p (float): Probability of applying the transform. Must be in the range\n            [0, 1]. Default: 0.5.\n\n    Raises:\n        NotImplementedError: If the input image has only one channel.\n        ValueError: If the upper bound of channel_drop_range is greater than or\n            equal to the number of channels in the input image.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ChannelDropout(channel_drop_range=(1, 2), fill=128, p=1.0)\n        >>> result = transform(image=image)\n        >>> dropped_image = result['image']\n        >>> assert dropped_image.shape == image.shape\n        >>> assert np.any(dropped_image != image)  # Some channels should be different\n\n    Note:\n        - The number of channels to drop is randomly chosen within the specified range.\n        - Channels are randomly selected for dropping.\n        - This transform is not applicable to single-channel (grayscale) images.\n        - The transform will raise an error if it's not possible to drop the specified\n          number of channels (e.g., trying to drop 3 channels from an RGB image).\n        - This augmentation can be particularly useful for training models to be robust\n          against missing or corrupted channel data in multi-spectral or hyperspectral imagery.\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        channel_drop_range: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        fill_value: float | None\n        fill: float\n\n        @model_validator(mode=\"after\")\n        def validate_fill(self) -> Self:\n            if self.fill_value is not None:\n                self.fill = self.fill_value\n                warn(\n                    \"`fill_value` deprecated. Use `fill` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            return self\n\n    def __init__(\n        self,\n        channel_drop_range: tuple[int, int] = (1, 1),\n        fill_value: float | None = None,\n        fill: float = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.channel_drop_range = channel_drop_range\n        self.fill = fill\n\n    def apply(self, img: np.ndarray, channels_to_drop: tuple[int, ...], **params: Any) -> np.ndarray:\n        return channel_dropout(img, channels_to_drop, self.fill)\n\n    def get_params_dependent_on_data(self, params: Mapping[str, Any], data: Mapping[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if num_channels == 1:\n            msg = \"Images has one channel. ChannelDropout is not defined.\"\n            raise NotImplementedError(msg)\n\n        if self.channel_drop_range[1] >= num_channels:\n            msg = \"Can not drop all channels in ChannelDropout.\"\n            raise ValueError(msg)\n\n        num_drop_channels = self.py_random.randint(*self.channel_drop_range)\n\n        channels_to_drop = self.py_random.sample(range(num_channels), k=num_drop_channels)\n\n        return {\"channels_to_drop\": channels_to_drop}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"channel_drop_range\", \"fill\"\n
"},{"location":"api_reference/augmentations/dropout/coarse_dropout/","title":"CoarseDropout augmentation (augmentations.dropout.coarse_dropout)","text":""},{"location":"api_reference/augmentations/dropout/coarse_dropout/#albumentations.augmentations.dropout.coarse_dropout.CoarseDropout","title":"class CoarseDropout (max_holes=None, max_height=None, max_width=None, min_holes=None, min_height=None, min_width=None, fill_value=None, mask_fill_value=None, num_holes_range=(1, 1), hole_height_range=(8, 8), hole_width_range=(8, 8), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

CoarseDropout randomly drops out rectangular regions from the image and optionally, the corresponding regions in an associated mask, to simulate occlusion and varied object sizes found in real-world settings.

This transformation is an evolution of CutOut and RandomErasing, offering more flexibility in the size, number of dropout regions, and fill values.

Parameters:

Name Type Description num_holes_range tuple[int, int]

Range (min, max) for the number of rectangular regions to drop out. Default: (1, 1)

hole_height_range tuple[Real, Real]

Range (min, max) for the height of dropout regions. If int, specifies absolute pixel values. If float, interpreted as a fraction of the image height. Default: (8, 8)

hole_width_range tuple[Real, Real]

Range (min, max) for the width of dropout regions. If int, specifies absolute pixel values. If float, interpreted as a fraction of the image width. Default: (8, 8)

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the mask. If None, mask regions corresponding to image dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges for each application.
  • When using float values for hole_height_range and hole_width_range, ensure they are between 0 and 1.
  • This implementation includes deprecation warnings for older parameter names (min_holes, max_holes, etc.).
  • Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.
  • For 'random_uniform' fill, each hole gets a single random color, unlike 'random' where each pixel gets its own random value.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> # Example with random uniform fill\n>>> aug_random = A.CoarseDropout(\n...     num_holes_range=(3, 6),\n...     hole_height_range=(10, 20),\n...     hole_width_range=(10, 20),\n...     fill=\"random_uniform\",\n...     p=1.0\n... )\n>>> # Example with inpainting\n>>> aug_inpaint = A.CoarseDropout(\n...     num_holes_range=(3, 6),\n...     hole_height_range=(10, 20),\n...     hole_width_range=(10, 20),\n...     fill=\"inpaint_ns\",\n...     p=1.0\n... )\n>>> transformed = aug_random(image=image, mask=mask)\n>>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n

References

  • CutOut: https://arxiv.org/abs/1708.04552
  • Random Erasing: https://arxiv.org/abs/1708.04896
  • OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/coarse_dropout.py Python
class CoarseDropout(BaseDropout):\n    \"\"\"CoarseDropout randomly drops out rectangular regions from the image and optionally,\n    the corresponding regions in an associated mask, to simulate occlusion and\n    varied object sizes found in real-world settings.\n\n    This transformation is an evolution of CutOut and RandomErasing, offering more\n    flexibility in the size, number of dropout regions, and fill values.\n\n    Args:\n        num_holes_range (tuple[int, int]): Range (min, max) for the number of rectangular\n            regions to drop out. Default: (1, 1)\n        hole_height_range (tuple[Real, Real]): Range (min, max) for the height\n            of dropout regions. If int, specifies absolute pixel values. If float,\n            interpreted as a fraction of the image height. Default: (8, 8)\n        hole_width_range (tuple[Real, Real]): Range (min, max) for the width\n            of dropout regions. If int, specifies absolute pixel values. If float,\n            interpreted as a fraction of the image width. Default: (8, 8)\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        fill_mask (ColorType | None): Fill value for dropout regions in the mask.\n            If None, mask regions corresponding to image dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual number and size of dropout regions are randomly chosen within the specified ranges for each\n            application.\n        - When using float values for hole_height_range and hole_width_range, ensure they are between 0 and 1.\n        - This implementation includes deprecation warnings for older parameter names (min_holes, max_holes, etc.).\n        - Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.\n        - For 'random_uniform' fill, each hole gets a single random color, unlike 'random' where each pixel\n            gets its own random value.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> # Example with random uniform fill\n        >>> aug_random = A.CoarseDropout(\n        ...     num_holes_range=(3, 6),\n        ...     hole_height_range=(10, 20),\n        ...     hole_width_range=(10, 20),\n        ...     fill=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> # Example with inpainting\n        >>> aug_inpaint = A.CoarseDropout(\n        ...     num_holes_range=(3, 6),\n        ...     hole_height_range=(10, 20),\n        ...     hole_width_range=(10, 20),\n        ...     fill=\"inpaint_ns\",\n        ...     p=1.0\n        ... )\n        >>> transformed = aug_random(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n\n    References:\n        - CutOut: https://arxiv.org/abs/1708.04552\n        - Random Erasing: https://arxiv.org/abs/1708.04896\n        - OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        min_holes: int | None = Field(ge=0)\n        max_holes: int | None = Field(ge=0)\n        num_holes_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n\n        min_height: ScalarType | None = Field(ge=0)\n        max_height: ScalarType | None = Field(ge=0)\n        hole_height_range: Annotated[\n            tuple[ScalarType, ScalarType],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(1, None)),\n        ]\n\n        min_width: ScalarType | None = Field(ge=0)\n        max_width: ScalarType | None = Field(ge=0)\n        hole_width_range: Annotated[\n            tuple[ScalarType, ScalarType],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(1, None)),\n        ]\n\n        @staticmethod\n        def update_range(\n            min_value: Number | None,\n            max_value: Number | None,\n            default_range: tuple[Number, Number],\n        ) -> tuple[Number, Number]:\n            return (min_value or max_value, max_value) if max_value is not None else default_range\n\n        @staticmethod\n        def validate_range(range_value: tuple[float, float], range_name: str, minimum: float = 0) -> None:\n            if not minimum <= range_value[0] <= range_value[1]:\n                raise ValueError(\n                    f\"First value in {range_name} should be less or equal than the second value \"\n                    f\"and at least {minimum}. Got: {range_value}\",\n                )\n            if isinstance(range_value[0], float) and not all(0 <= x <= 1 for x in range_value):\n                raise ValueError(f\"All values in {range_name} should be in [0, 1] range. Got: {range_value}\")\n\n        @model_validator(mode=\"after\")\n        def check_num_holes_and_dimensions(self) -> Self:\n            if self.min_holes is not None:\n                warn(\"`min_holes` is deprecated. Use num_holes_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_holes is not None:\n                warn(\"`max_holes` is deprecated. Use num_holes_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.min_height is not None:\n                warn(\"`min_height` is deprecated. Use hole_height_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_height is not None:\n                warn(\"`max_height` is deprecated. Use hole_height_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.min_width is not None:\n                warn(\"`min_width` is deprecated. Use hole_width_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_width is not None:\n                warn(\"`max_width` is deprecated. Use hole_width_range instead.\", DeprecationWarning, stacklevel=2)\n\n            if self.max_holes is not None:\n                self.num_holes_range = self.update_range(self.min_holes, self.max_holes, self.num_holes_range)\n\n            self.validate_range(self.num_holes_range, \"num_holes_range\", minimum=1)\n\n            if self.max_height is not None:\n                self.hole_height_range = self.update_range(self.min_height, self.max_height, self.hole_height_range)\n            self.validate_range(self.hole_height_range, \"hole_height_range\")\n\n            if self.max_width is not None:\n                self.hole_width_range = self.update_range(self.min_width, self.max_width, self.hole_width_range)\n            self.validate_range(self.hole_width_range, \"hole_width_range\")\n\n            return self\n\n    def __init__(\n        self,\n        max_holes: int | None = None,\n        max_height: ScalarType | None = None,\n        max_width: ScalarType | None = None,\n        min_holes: int | None = None,\n        min_height: ScalarType | None = None,\n        min_width: ScalarType | None = None,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        num_holes_range: tuple[int, int] = (1, 1),\n        hole_height_range: tuple[ScalarType, ScalarType] = (8, 8),\n        hole_width_range: tuple[ScalarType, ScalarType] = (8, 8),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.num_holes_range = num_holes_range\n        self.hole_height_range = hole_height_range\n        self.hole_width_range = hole_width_range\n\n    def calculate_hole_dimensions(\n        self,\n        image_shape: tuple[int, int],\n        height_range: tuple[float, float],\n        width_range: tuple[float, float],\n        size: int,\n    ) -> tuple[np.ndarray, np.ndarray]:\n        \"\"\"Calculate random hole dimensions based on the provided ranges.\"\"\"\n        height, width = image_shape[:2]\n\n        if isinstance(height_range[0], int):\n            min_height = height_range[0]\n            max_height = min(height_range[1], height)\n\n            min_width = width_range[0]\n            max_width = min(width_range[1], width)\n\n            hole_heights = self.random_generator.integers(int(min_height), int(max_height + 1), size=size)\n            hole_widths = self.random_generator.integers(int(min_width), int(max_width + 1), size=size)\n\n        else:  # Assume float\n            hole_heights = (height * self.random_generator.uniform(*height_range, size=size)).astype(int)\n            hole_widths = (width * self.random_generator.uniform(*width_range, size=size)).astype(int)\n\n        return hole_heights, hole_widths\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        num_holes = self.py_random.randint(*self.num_holes_range)\n\n        hole_heights, hole_widths = self.calculate_hole_dimensions(\n            image_shape,\n            self.hole_height_range,\n            self.hole_width_range,\n            size=num_holes,\n        )\n\n        height, width = image_shape[:2]\n\n        y_min = self.random_generator.integers(0, height - hole_heights + 1, size=num_holes)\n        x_min = self.random_generator.integers(0, width - hole_widths + 1, size=num_holes)\n        y_max = y_min + hole_heights\n        x_max = x_min + hole_widths\n\n        holes = np.stack([x_min, y_min, x_max, y_max], axis=-1)\n\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"num_holes_range\", \"hole_height_range\", \"hole_width_range\")\n
"},{"location":"api_reference/augmentations/dropout/coarse_dropout/#albumentations.augmentations.dropout.coarse_dropout.Erasing","title":"class Erasing (scale=(0.02, 0.33), ratio=(0.3, 3.3), fill=0, fill_mask=None, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly erases rectangular regions in an image, following the Random Erasing Data Augmentation technique.

This augmentation helps improve model robustness by randomly masking out rectangular regions in the image, simulating occlusions and encouraging the model to learn from partial information. It's particularly effective for image classification and person re-identification tasks.

Parameters:

Name Type Description scale tuple[float, float]

Range for the proportion of image area to erase. The actual area will be randomly sampled from (scale[0] * image_area, scale[1] * image_area). Default: (0.02, 0.33)

ratio tuple[float, float]

Range for the aspect ratio (width/height) of the erased region. The actual ratio will be randomly sampled from (ratio[0], ratio[1]). Default: (0.3, 3.3)

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value used to fill the erased regions. Can be: - int or float: fills all channels with this value - tuple: fills each channel with corresponding value - \"random\": fills each pixel with random values - \"random_uniform\": fills entire erased region with a single random color - \"inpaint_telea\": uses OpenCV Telea inpainting method - \"inpaint_ns\": uses OpenCV Navier-Stokes inpainting method Default: 0

mask_fill ColorType | None

Value used to fill erased regions in the mask. If None, mask regions are not modified. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform attempts to find valid erasing parameters up to 10 times. If unsuccessful, no erasing is performed.
  • The actual erased area and aspect ratio are randomly sampled within the specified ranges for each application.
  • When using inpainting methods, only grayscale or RGB images are supported.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Basic usage with default parameters\n>>> transform = A.Erasing()\n>>> transformed = transform(image=image)\n>>> # Custom configuration\n>>> transform = A.Erasing(\n...     scale=(0.1, 0.4),\n...     ratio=(0.5, 2.0),\n...     fill_value=\"random_uniform\",\n...     p=1.0\n... )\n>>> transformed = transform(image=image)\n

References

  • Paper: https://arxiv.org/abs/1708.04896
  • Implementation inspired by torchvision: https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.RandomErasing

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/coarse_dropout.py Python
class Erasing(BaseDropout):\n    \"\"\"Randomly erases rectangular regions in an image, following the Random Erasing Data Augmentation technique.\n\n    This augmentation helps improve model robustness by randomly masking out rectangular regions in the image,\n    simulating occlusions and encouraging the model to learn from partial information. It's particularly\n    effective for image classification and person re-identification tasks.\n\n    Args:\n        scale (tuple[float, float]): Range for the proportion of image area to erase.\n            The actual area will be randomly sampled from (scale[0] * image_area, scale[1] * image_area).\n            Default: (0.02, 0.33)\n        ratio (tuple[float, float]): Range for the aspect ratio (width/height) of the erased region.\n            The actual ratio will be randomly sampled from (ratio[0], ratio[1]).\n            Default: (0.3, 3.3)\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value used to fill the erased regions. Can be:\n            - int or float: fills all channels with this value\n            - tuple: fills each channel with corresponding value\n            - \"random\": fills each pixel with random values\n            - \"random_uniform\": fills entire erased region with a single random color\n            - \"inpaint_telea\": uses OpenCV Telea inpainting method\n            - \"inpaint_ns\": uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        mask_fill (ColorType | None): Value used to fill erased regions in the mask.\n            If None, mask regions are not modified. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform attempts to find valid erasing parameters up to 10 times.\n          If unsuccessful, no erasing is performed.\n        - The actual erased area and aspect ratio are randomly sampled within\n          the specified ranges for each application.\n        - When using inpainting methods, only grayscale or RGB images are supported.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Basic usage with default parameters\n        >>> transform = A.Erasing()\n        >>> transformed = transform(image=image)\n        >>> # Custom configuration\n        >>> transform = A.Erasing(\n        ...     scale=(0.1, 0.4),\n        ...     ratio=(0.5, 2.0),\n        ...     fill_value=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> transformed = transform(image=image)\n\n    References:\n        - Paper: https://arxiv.org/abs/1708.04896\n        - Implementation inspired by torchvision:\n          https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.RandomErasing\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        scale: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n        ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n\n    def __init__(\n        self,\n        scale: tuple[float, float] = (0.02, 0.33),\n        ratio: tuple[float, float] = (0.3, 3.3),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n\n        self.scale = scale\n        self.ratio = ratio\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Calculate erasing parameters using direct mathematical derivation.\n\n        Given:\n        - Image dimensions (H, W)\n        - Target area (A)\n        - Aspect ratio (r = w/h)\n\n        We know:\n        - h * w = A (area equation)\n        - w = r * h (aspect ratio equation)\n\n        Therefore:\n        - h * (r * h) = A\n        - h\u00b2 = A/r\n        - h = sqrt(A/r)\n        - w = r * sqrt(A/r) = sqrt(A*r)\n        \"\"\"\n        height, width = params[\"shape\"][:2]\n        total_area = height * width\n\n        # Calculate maximum valid area based on dimensions and aspect ratio\n        max_area = total_area * self.scale[1]\n        min_area = total_area * self.scale[0]\n\n        # For each aspect ratio r, the maximum area is constrained by:\n        # h = sqrt(A/r) \u2264 H and w = sqrt(A*r) \u2264 W\n        # Therefore: A \u2264 min(r*H\u00b2, W\u00b2/r)\n        r_min, r_max = self.ratio\n\n        def area_constraint_h(r: float) -> float:\n            return r * height * height\n\n        def area_constraint_w(r: float) -> float:\n            return width * width / r\n\n        # Find maximum valid area considering aspect ratio constraints\n        max_area_h = min(area_constraint_h(r_min), area_constraint_h(r_max))\n        max_area_w = min(area_constraint_w(r_min), area_constraint_w(r_max))\n        max_valid_area = min(max_area, max_area_h, max_area_w)\n\n        if max_valid_area < min_area:\n            return {\"holes\": np.array([], dtype=np.int32).reshape((0, 4))}\n\n        # Sample valid area and aspect ratio\n        erase_area = self.py_random.uniform(min_area, max_valid_area)\n\n        # Calculate valid aspect ratio range for this area\n        max_r = min(r_max, width * width / erase_area)\n        min_r = max(r_min, erase_area / (height * height))\n\n        if min_r > max_r:\n            return {\"holes\": np.array([], dtype=np.int32).reshape((0, 4))}\n\n        aspect_ratio = self.py_random.uniform(min_r, max_r)\n\n        # Calculate dimensions\n        h = int(round(np.sqrt(erase_area / aspect_ratio)))\n        w = int(round(np.sqrt(erase_area * aspect_ratio)))\n\n        # Sample position\n        top = self.py_random.randint(0, height - h)\n        left = self.py_random.randint(0, width - w)\n\n        holes = np.array([[left, top, left + w, top + h]], dtype=np.int32)\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"scale\", \"ratio\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/dropout/functional/","title":"Geometric functional transforms (augmentations.dropout.functional)","text":""},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.apply_inpainting","title":"def apply_inpainting (img, holes, method) [view source on GitHub]","text":"

Apply OpenCV inpainting to fill the holes in the image.

Parameters:

Name Type Description img np.ndarray

Input image (grayscale or BGR)

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

method InpaintMethod

Inpainting method to use (\"inpaint_telea\" or \"inpaint_ns\")

Returns:

Type Description np.ndarray

Inpainted image

Exceptions:

Type Description NotImplementedError

If image has more than 3 channels

Source code in albumentations/augmentations/dropout/functional.py Python
@uint8_io\ndef apply_inpainting(img: np.ndarray, holes: np.ndarray, method: InpaintMethod) -> np.ndarray:\n    \"\"\"Apply OpenCV inpainting to fill the holes in the image.\n\n    Args:\n        img: Input image (grayscale or BGR)\n        holes: Array of [x1, y1, x2, y2] coordinates\n        method: Inpainting method to use (\"inpaint_telea\" or \"inpaint_ns\")\n\n    Returns:\n        np.ndarray: Inpainted image\n\n    Raises:\n        NotImplementedError: If image has more than 3 channels\n    \"\"\"\n    num_channels = get_num_channels(img)\n    # Create inpainting mask\n    mask = np.zeros(img.shape[:2], dtype=np.uint8)\n    for x_min, y_min, x_max, y_max in holes:\n        mask[y_min:y_max, x_min:x_max] = 255\n\n    inpaint_method = cv2.INPAINT_TELEA if method == \"inpaint_telea\" else cv2.INPAINT_NS\n\n    # Handle grayscale images by converting to 3 channels and back\n    if num_channels == 1:\n        if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n            img = img.squeeze()\n        img_3ch = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)\n        result = cv2.inpaint(img_3ch, mask, 3, inpaint_method)\n        return (\n            cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)[..., None]\n            if num_channels == NUM_MULTI_CHANNEL_DIMENSIONS\n            else cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)\n        )\n\n    return cv2.inpaint(img, mask, 3, inpaint_method)\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.calculate_grid_dimensions","title":"def calculate_grid_dimensions (image_shape, unit_size_range, holes_number_xy, random_generator) [view source on GitHub]","text":"

Calculate the dimensions of grid units for GridDropout.

This function determines the size of grid units based on the input parameters. It supports three modes of operation: 1. Using a range of unit sizes 2. Using a specified number of holes in x and y directions 3. Falling back to a default calculation

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

unit_size_range tuple[int, int] | None

A range of possible unit sizes. If provided, a random size within this range will be chosen for both height and width.

holes_number_xy tuple[int, int] | None

The number of holes in the x and y directions. If provided, the grid dimensions will be calculated to fit this number of holes.

random_generator np.random.Generator

The random generator to use for generating random values.

Returns:

Type Description tuple[int, int]

The calculated grid unit dimensions as (unit_height, unit_width).

Exceptions:

Type Description ValueError

If the upper limit of unit_size_range is greater than the shortest image edge.

Notes

  • If both unit_size_range and holes_number_xy are None, the function falls back to a default calculation, where the grid unit size is set to max(2, image_dimension // 10) for both height and width.
  • The function prioritizes unit_size_range over holes_number_xy if both are provided.
  • When using holes_number_xy, the actual number of holes may be slightly different due to integer division.

Examples:

Python
>>> image_shape = (100, 200)\n>>> calculate_grid_dimensions(image_shape, unit_size_range=(10, 20))\n(15, 15)  # Random value between 10 and 20\n
Python
>>> calculate_grid_dimensions(image_shape, holes_number_xy=(5, 10))\n(20, 20)  # 100 // 5 and 200 // 10\n
Python
>>> calculate_grid_dimensions(image_shape)\n(10, 20)  # Default calculation: max(2, dimension // 10)\n
Source code in albumentations/augmentations/dropout/functional.py Python
def calculate_grid_dimensions(\n    image_shape: tuple[int, int],\n    unit_size_range: tuple[int, int] | None,\n    holes_number_xy: tuple[int, int] | None,\n    random_generator: np.random.Generator,\n) -> tuple[int, int]:\n    \"\"\"Calculate the dimensions of grid units for GridDropout.\n\n    This function determines the size of grid units based on the input parameters.\n    It supports three modes of operation:\n    1. Using a range of unit sizes\n    2. Using a specified number of holes in x and y directions\n    3. Falling back to a default calculation\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        unit_size_range (tuple[int, int] | None, optional): A range of possible unit sizes.\n            If provided, a random size within this range will be chosen for both height and width.\n        holes_number_xy (tuple[int, int] | None, optional): The number of holes in the x and y directions.\n            If provided, the grid dimensions will be calculated to fit this number of holes.\n        random_generator (np.random.Generator): The random generator to use for generating random values.\n\n    Returns:\n        tuple[int, int]: The calculated grid unit dimensions as (unit_height, unit_width).\n\n    Raises:\n        ValueError: If the upper limit of unit_size_range is greater than the shortest image edge.\n\n    Notes:\n        - If both unit_size_range and holes_number_xy are None, the function falls back to a default calculation,\n          where the grid unit size is set to max(2, image_dimension // 10) for both height and width.\n        - The function prioritizes unit_size_range over holes_number_xy if both are provided.\n        - When using holes_number_xy, the actual number of holes may be slightly different due to integer division.\n\n    Examples:\n        >>> image_shape = (100, 200)\n        >>> calculate_grid_dimensions(image_shape, unit_size_range=(10, 20))\n        (15, 15)  # Random value between 10 and 20\n\n        >>> calculate_grid_dimensions(image_shape, holes_number_xy=(5, 10))\n        (20, 20)  # 100 // 5 and 200 // 10\n\n        >>> calculate_grid_dimensions(image_shape)\n        (10, 20)  # Default calculation: max(2, dimension // 10)\n    \"\"\"\n    height, width = image_shape[:2]\n\n    if unit_size_range is not None:\n        if unit_size_range[1] > min(image_shape[:2]):\n            raise ValueError(\"Grid size limits must be within the shortest image edge.\")\n        unit_size = random_generator.integers(*unit_size_range)\n        return unit_size, unit_size\n\n    if holes_number_xy:\n        holes_number_x, holes_number_y = holes_number_xy\n        unit_width = width // holes_number_x\n        unit_height = height // holes_number_y\n        return unit_height, unit_width\n\n    # Default fallback\n    unit_width = max(2, width // 10)\n    unit_height = max(2, height // 10)\n    return unit_height, unit_width\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.cutout","title":"def cutout (img, holes, fill_value, random_generator) [view source on GitHub]","text":"

Apply cutout augmentation to the image by cutting out holes and filling them.

Parameters:

Name Type Description img np.ndarray

The image to augment

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

fill_value DropoutFillValue

Value to fill holes with. Can be: - number (int/float): Will be broadcast to all channels - sequence (tuple/list/ndarray): Must match number of channels - \"random\": Different random values for each pixel - \"random_uniform\": Same random value for entire hole - \"inpaint_telea\"/\"inpaint_ns\": OpenCV inpainting methods

random_generator np.random.Generator

Random number generator for random fills

Exceptions:

Type Description ValueError

If fill_value length doesn't match number of channels

Source code in albumentations/augmentations/dropout/functional.py Python
def cutout(\n    img: np.ndarray,\n    holes: np.ndarray,\n    fill_value: DropoutFillValue,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply cutout augmentation to the image by cutting out holes and filling them.\n\n    Args:\n        img: The image to augment\n        holes: Array of [x1, y1, x2, y2] coordinates\n        fill_value: Value to fill holes with. Can be:\n            - number (int/float): Will be broadcast to all channels\n            - sequence (tuple/list/ndarray): Must match number of channels\n            - \"random\": Different random values for each pixel\n            - \"random_uniform\": Same random value for entire hole\n            - \"inpaint_telea\"/\"inpaint_ns\": OpenCV inpainting methods\n        random_generator: Random number generator for random fills\n\n    Raises:\n        ValueError: If fill_value length doesn't match number of channels\n    \"\"\"\n    img = img.copy()\n\n    # Handle inpainting methods\n    if isinstance(fill_value, str):\n        if fill_value in {\"inpaint_telea\", \"inpaint_ns\"}:\n            return apply_inpainting(img, holes, cast(InpaintMethod, fill_value))\n        if fill_value == \"random\":\n            return fill_holes_with_random(img, holes, random_generator, uniform=False)\n        if fill_value == \"random_uniform\":\n            return fill_holes_with_random(img, holes, random_generator, uniform=True)\n        raise ValueError(f\"Unsupported string fill_value: {fill_value}\")\n\n    # Convert numeric fill values to numpy array\n    if isinstance(fill_value, (int, float)):\n        fill_array = np.array(fill_value, dtype=img.dtype)\n        return fill_holes_with_value(img, holes, fill_array)\n\n    # Handle sequence fill values\n    fill_array = np.array(fill_value, dtype=img.dtype)\n\n    # For multi-channel images, verify fill_value matches number of channels\n    if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n        fill_array = fill_array.ravel()\n        if fill_array.size != img.shape[2]:\n            raise ValueError(\n                f\"Fill value must have same number of channels as image. \"\n                f\"Got {fill_array.size}, expected {img.shape[2]}\",\n            )\n\n    return fill_holes_with_value(img, holes, fill_array)\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.fill_holes_with_random","title":"def fill_holes_with_random (img, holes, random_generator, uniform) [view source on GitHub]","text":"

Fill holes with random values.

Parameters:

Name Type Description img np.ndarray

Input image

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

random_generator np.random.Generator

Random number generator

uniform bool

If True, use same random value for entire hole

Source code in albumentations/augmentations/dropout/functional.py Python
def fill_holes_with_random(\n    img: np.ndarray,\n    holes: np.ndarray,\n    random_generator: np.random.Generator,\n    uniform: bool,\n) -> np.ndarray:\n    \"\"\"Fill holes with random values.\n\n    Args:\n        img: Input image\n        holes: Array of [x1, y1, x2, y2] coordinates\n        random_generator: Random number generator\n        uniform: If True, use same random value for entire hole\n    \"\"\"\n    for x_min, y_min, x_max, y_max in holes:\n        shape = (1,) if uniform else (y_max - y_min, x_max - x_min)\n        if img.ndim != MONO_CHANNEL_DIMENSIONS:\n            shape = (1, img.shape[2]) if uniform else (*shape, img.shape[2])\n\n        random_fill = generate_random_fill(img.dtype, shape, random_generator)\n        img[y_min:y_max, x_min:x_max] = random_fill\n    return img\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.fill_holes_with_value","title":"def fill_holes_with_value (img, holes, fill_value) [view source on GitHub]","text":"

Fill holes with a constant value.

Parameters:

Name Type Description img np.ndarray

Input image

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

fill_value np.ndarray

Value to fill the holes with

Source code in albumentations/augmentations/dropout/functional.py Python
def fill_holes_with_value(img: np.ndarray, holes: np.ndarray, fill_value: np.ndarray) -> np.ndarray:\n    \"\"\"Fill holes with a constant value.\n\n    Args:\n        img: Input image\n        holes: Array of [x1, y1, x2, y2] coordinates\n        fill_value: Value to fill the holes with\n    \"\"\"\n    for x_min, y_min, x_max, y_max in holes:\n        img[y_min:y_max, x_min:x_max] = fill_value\n    return img\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.filter_bboxes_by_holes","title":"def filter_bboxes_by_holes (bboxes, holes, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Filter bounding boxes based on their remaining visible area and visibility ratio after intersection with holes.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes, each represented as [x_min, y_min, x_max, y_max].

holes np.ndarray

Array of holes, each represented as [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image (height, width).

min_area int

Minimum remaining visible area to keep the bounding box.

min_visibility float

Minimum visibility ratio to keep the bounding box. Calculated as 1 - (intersection_area / bbox_area).

Returns:

Type Description np.ndarray

Filtered array of bounding boxes.

Source code in albumentations/augmentations/dropout/functional.py Python
def filter_bboxes_by_holes(\n    bboxes: np.ndarray,\n    holes: np.ndarray,\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Filter bounding boxes based on their remaining visible area and visibility ratio after intersection with holes.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes, each represented as [x_min, y_min, x_max, y_max].\n        holes (np.ndarray): Array of holes, each represented as [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        min_area (int): Minimum remaining visible area to keep the bounding box.\n        min_visibility (float): Minimum visibility ratio to keep the bounding box.\n            Calculated as 1 - (intersection_area / bbox_area).\n\n    Returns:\n        np.ndarray: Filtered array of bounding boxes.\n    \"\"\"\n    if len(bboxes) == 0 or len(holes) == 0:\n        return bboxes\n\n    # Create a blank mask for holes\n    hole_mask = np.zeros(image_shape, dtype=np.uint8)\n\n    # Fill in the holes on the mask\n    for hole in holes:\n        x_min, y_min, x_max, y_max = hole.astype(int)\n        hole_mask[y_min:y_max, x_min:x_max] = 1\n\n    # Vectorized calculation\n    bboxes_int = bboxes.astype(int)\n    x_min, y_min, x_max, y_max = bboxes_int[:, 0], bboxes_int[:, 1], bboxes_int[:, 2], bboxes_int[:, 3]\n\n    # Calculate box areas\n    box_areas = (x_max - x_min) * (y_max - y_min)\n\n    # Create a mask of the same shape as bboxes\n    mask = np.zeros(len(bboxes), dtype=bool)\n\n    for i in range(len(bboxes)):\n        intersection_area = np.sum(hole_mask[y_min[i] : y_max[i], x_min[i] : x_max[i]])\n        remaining_area = box_areas[i] - intersection_area\n        visibility_ratio = 1 - (intersection_area / box_areas[i])\n        mask[i] = (remaining_area >= min_area) and (visibility_ratio >= min_visibility)\n\n    return bboxes[mask]\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.filter_keypoints_in_holes","title":"def filter_keypoints_in_holes (keypoints, holes) [view source on GitHub]","text":"

Filter out keypoints that are inside any of the holes.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (num_keypoints, 2+). The first two columns are x and y coordinates.

holes np.ndarray

Array of holes with shape (num_holes, 4). Each hole is represented as [x1, y1, x2, y2].

Returns:

Type Description np.ndarray

Array of keypoints that are not inside any hole.

Source code in albumentations/augmentations/dropout/functional.py Python
@handle_empty_array(\"keypoints\")\ndef filter_keypoints_in_holes(keypoints: np.ndarray, holes: np.ndarray) -> np.ndarray:\n    \"\"\"Filter out keypoints that are inside any of the holes.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (num_keypoints, 2+).\n                                The first two columns are x and y coordinates.\n        holes (np.ndarray): Array of holes with shape (num_holes, 4).\n                            Each hole is represented as [x1, y1, x2, y2].\n\n    Returns:\n        np.ndarray: Array of keypoints that are not inside any hole.\n    \"\"\"\n    # Broadcast keypoints and holes for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    hole_x1 = holes[:, 0]  # Shape: (num_holes,)\n    hole_y1 = holes[:, 1]  # Shape: (num_holes,)\n    hole_x2 = holes[:, 2]  # Shape: (num_holes,)\n    hole_y2 = holes[:, 3]  # Shape: (num_holes,)\n\n    # Check if each keypoint is inside each hole\n    inside_hole = (kp_x >= hole_x1) & (kp_x < hole_x2) & (kp_y >= hole_y1) & (kp_y < hole_y2)\n\n    # A keypoint is valid if it's not inside any hole\n    valid_keypoints = ~np.any(inside_hole, axis=1)\n\n    return keypoints[valid_keypoints]\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.generate_grid_holes","title":"def generate_grid_holes (image_shape, grid, ratio, random_offset, shift_xy, random_generator) [view source on GitHub]","text":"

Generate a list of holes for GridDropout using a uniform grid.

This function creates a grid of holes for use in the GridDropout augmentation technique. It allows for customization of the grid size, hole size ratio, and positioning of holes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns). This determines the number of cells in the grid, where each cell may contain a hole.

ratio float

The ratio of the hole size to the grid cell size. Should be between 0 and 1. A ratio of 1 means the hole will fill the entire grid cell.

random_offset bool

If True, applies random offsets to each hole within its grid cell. If False, uses the global shift specified by shift_xy.

shift_xy tuple[int, int]

The global shift to apply to all holes as (shift_x, shift_y). Only used when random_offset is False.

random_generator np.random.Generator

The random generator for generating random offsets and shuffling. If None, a new Generator will be created.

Returns:

Type Description np.ndarray

An array of hole coordinates, where each hole is represented as [x1, y1, x2, y2]. The shape of the array is (n_holes, 4), where n_holes is determined by the grid size.

Notes

  • The function first creates a uniform grid based on the image shape and specified grid size.
  • Hole sizes are calculated based on the provided ratio and grid cell sizes.
  • If random_offset is True, each hole is randomly positioned within its grid cell.
  • If random_offset is False, all holes are shifted by the global shift_xy value.
  • The function ensures that all holes remain within the image boundaries.

Examples:

Python
>>> image_shape = (100, 100)\n>>> grid = (5, 5)\n>>> ratio = 0.5\n>>> random_offset = True\n>>> random_state = np.random.RandomState(42)\n>>> shift_xy = (0, 0)\n>>> holes = generate_grid_holes(image_shape, grid, ratio, random_offset, random_state, shift_xy)\n>>> print(holes.shape)\n(25, 4)\n>>> print(holes[0])  # Example output: [x1, y1, x2, y2] of the first hole\n[ 1 21 11 31]\n
Source code in albumentations/augmentations/dropout/functional.py Python
def generate_grid_holes(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    ratio: float,\n    random_offset: bool,\n    shift_xy: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a list of holes for GridDropout using a uniform grid.\n\n    This function creates a grid of holes for use in the GridDropout augmentation technique.\n    It allows for customization of the grid size, hole size ratio, and positioning of holes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns). This determines the number of cells\n            in the grid, where each cell may contain a hole.\n        ratio (float): The ratio of the hole size to the grid cell size. Should be between 0 and 1.\n            A ratio of 1 means the hole will fill the entire grid cell.\n        random_offset (bool): If True, applies random offsets to each hole within its grid cell.\n            If False, uses the global shift specified by shift_xy.\n        shift_xy (tuple[int, int]): The global shift to apply to all holes as (shift_x, shift_y).\n            Only used when random_offset is False.\n        random_generator (np.random.Generator): The random generator for generating random offsets\n            and shuffling. If None, a new Generator will be created.\n\n    Returns:\n        np.ndarray: An array of hole coordinates, where each hole is represented as\n            [x1, y1, x2, y2]. The shape of the array is (n_holes, 4), where n_holes\n            is determined by the grid size.\n\n    Notes:\n        - The function first creates a uniform grid based on the image shape and specified grid size.\n        - Hole sizes are calculated based on the provided ratio and grid cell sizes.\n        - If random_offset is True, each hole is randomly positioned within its grid cell.\n        - If random_offset is False, all holes are shifted by the global shift_xy value.\n        - The function ensures that all holes remain within the image boundaries.\n\n    Examples:\n        >>> image_shape = (100, 100)\n        >>> grid = (5, 5)\n        >>> ratio = 0.5\n        >>> random_offset = True\n        >>> random_state = np.random.RandomState(42)\n        >>> shift_xy = (0, 0)\n        >>> holes = generate_grid_holes(image_shape, grid, ratio, random_offset, random_state, shift_xy)\n        >>> print(holes.shape)\n        (25, 4)\n        >>> print(holes[0])  # Example output: [x1, y1, x2, y2] of the first hole\n        [ 1 21 11 31]\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Generate the uniform grid\n    cells = split_uniform_grid(image_shape, grid, random_generator)\n\n    # Calculate hole sizes based on the ratio\n    cell_heights = cells[:, 2] - cells[:, 0]\n    cell_widths = cells[:, 3] - cells[:, 1]\n    hole_heights = np.clip(cell_heights * ratio, 1, cell_heights - 1).astype(int)\n    hole_widths = np.clip(cell_widths * ratio, 1, cell_widths - 1).astype(int)\n\n    # Calculate maximum possible offsets\n    max_offset_y = cell_heights - hole_heights\n    max_offset_x = cell_widths - hole_widths\n\n    if random_offset:\n        # Generate random offsets for each hole\n        offset_y = random_generator.integers(0, max_offset_y + 1)\n        offset_x = random_generator.integers(0, max_offset_x + 1)\n    else:\n        # Use global shift\n        offset_y = np.full_like(max_offset_y, shift_xy[1])\n        offset_x = np.full_like(max_offset_x, shift_xy[0])\n\n    # Calculate hole coordinates\n    x_min = np.clip(cells[:, 1] + offset_x, 0, width - hole_widths)\n    y_min = np.clip(cells[:, 0] + offset_y, 0, height - hole_heights)\n    x_max = np.minimum(x_min + hole_widths, width)\n    y_max = np.minimum(y_min + hole_heights, height)\n\n    return np.column_stack((x_min, y_min, x_max, y_max))\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.generate_random_fill","title":"def generate_random_fill (dtype, shape, random_generator) [view source on GitHub]","text":"

Generate a random fill array based on the given dtype and target shape.

This function creates a numpy array filled with random values. The range and type of these values depend on the input dtype. For integer dtypes, it generates random integers. For floating-point dtypes, it generates random floats.

Parameters:

Name Type Description dtype np.dtype

The data type of the array to be generated.

shape tuple[int, ...]

The shape of the array to be generated.

random_generator np.random.Generator

The random generator to use for generating values. If None, the default numpy random generator is used.

Returns:

Type Description np.ndarray

A numpy array of the specified shape and dtype, filled with random values.

Exceptions:

Type Description ValueError

If the input dtype is neither integer nor floating-point.

Examples:

Python
>>> import numpy as np\n>>> random_state = np.random.RandomState(42)\n>>> result = generate_random_fill(np.dtype('uint8'), (2, 2), random_state)\n>>> print(result)\n[[172 251]\n [ 80 141]]\n
Source code in albumentations/augmentations/dropout/functional.py Python
def generate_random_fill(\n    dtype: np.dtype,\n    shape: tuple[int, ...],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a random fill array based on the given dtype and target shape.\n\n    This function creates a numpy array filled with random values. The range and type of these values\n    depend on the input dtype. For integer dtypes, it generates random integers. For floating-point\n    dtypes, it generates random floats.\n\n    Args:\n        dtype (np.dtype): The data type of the array to be generated.\n        shape (tuple[int, ...]): The shape of the array to be generated.\n        random_generator (np.random.Generator): The random generator to use for generating values.\n            If None, the default numpy random generator is used.\n\n    Returns:\n        np.ndarray: A numpy array of the specified shape and dtype, filled with random values.\n\n    Raises:\n        ValueError: If the input dtype is neither integer nor floating-point.\n\n    Examples:\n        >>> import numpy as np\n        >>> random_state = np.random.RandomState(42)\n        >>> result = generate_random_fill(np.dtype('uint8'), (2, 2), random_state)\n        >>> print(result)\n        [[172 251]\n         [ 80 141]]\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[dtype]\n    if np.issubdtype(dtype, np.integer):\n        return random_generator.integers(0, max_value + 1, size=shape, dtype=dtype)\n    if np.issubdtype(dtype, np.floating):\n        return random_generator.uniform(0, max_value, size=shape).astype(dtype)\n    raise ValueError(f\"Unsupported dtype: {dtype}\")\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.label","title":"def label (mask, return_num=False, connectivity=2) [view source on GitHub]","text":"

Label connected regions of an integer array.

This function uses OpenCV's connectedComponents under the hood but mimics the behavior of scikit-image's label function.

Parameters:

Name Type Description mask np.ndarray

The array to label. Must be of integer type.

return_num bool

If True, return the number of labels (default: False).

connectivity int

Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. Accepted values are 1 or 2. Default is 2.

Returns:

Type Description np.ndarray | tuple[np.ndarray, int]

Labeled array, where all connected regions are assigned the same integer value. If return_num is True, it also returns the number of labels.

Source code in albumentations/augmentations/dropout/functional.py Python
def label(mask: np.ndarray, return_num: bool = False, connectivity: int = 2) -> np.ndarray | tuple[np.ndarray, int]:\n    \"\"\"Label connected regions of an integer array.\n\n    This function uses OpenCV's connectedComponents under the hood but mimics\n    the behavior of scikit-image's label function.\n\n    Args:\n        mask (np.ndarray): The array to label. Must be of integer type.\n        return_num (bool): If True, return the number of labels (default: False).\n        connectivity (int): Maximum number of orthogonal hops to consider a pixel/voxel\n                            as a neighbor. Accepted values are 1 or 2. Default is 2.\n\n    Returns:\n        np.ndarray | tuple[np.ndarray, int]: Labeled array, where all connected regions are\n        assigned the same integer value. If return_num is True, it also returns the number of labels.\n    \"\"\"\n    # Create a copy of the original mask\n    labeled = np.zeros_like(mask, dtype=np.int32)\n\n    # Get unique non-zero values from the original mask\n    unique_values = np.unique(mask[mask != 0])\n\n    # Label each unique value separately\n    next_label = 1\n    for value in unique_values:\n        binary_mask = (mask == value).astype(np.uint8)\n\n        # Set connectivity for OpenCV (4 or 8)\n        cv2_connectivity = 4 if connectivity == 1 else 8\n\n        # Use OpenCV's connectedComponents\n        num_labels, labels = cv2.connectedComponents(binary_mask, connectivity=cv2_connectivity)\n\n        # Assign new labels\n        for i in range(1, num_labels):\n            labeled[labels == i] = next_label\n            next_label += 1\n\n    num_labels = next_label - 1\n\n    return (labeled, num_labels) if return_num else labeled\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.mask_dropout_bboxes","title":"def mask_dropout_bboxes (bboxes, dropout_mask, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Filter out bounding boxes based on their intersection with the dropout mask.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) in format [x_min, y_min, x_max, y_max, ...].

dropout_mask np.ndarray

Boolean mask of shape (height, width) where True values indicate dropped out regions.

image_shape Tuple[int, int]

The shape of the original image as (height, width).

min_area float

Minimum area of the bounding box to be kept.

min_visibility float

Minimum visibility ratio of the bounding box to be kept.

Returns:

Type Description np.ndarray

Filtered array of bounding boxes.

Source code in albumentations/augmentations/dropout/functional.py Python
@handle_empty_array(\"bboxes\")\ndef mask_dropout_bboxes(\n    bboxes: np.ndarray,\n    dropout_mask: np.ndarray,\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Filter out bounding boxes based on their intersection with the dropout mask.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (N, 4+) in format [x_min, y_min, x_max, y_max, ...].\n        dropout_mask (np.ndarray): Boolean mask of shape (height, width) where True values indicate dropped out regions.\n        image_shape (Tuple[int, int]): The shape of the original image as (height, width).\n        min_area (float): Minimum area of the bounding box to be kept.\n        min_visibility (float): Minimum visibility ratio of the bounding box to be kept.\n\n    Returns:\n        np.ndarray: Filtered array of bounding boxes.\n    \"\"\"\n    height, width = image_shape\n\n    # Create binary masks for each bounding box\n    y, x = np.ogrid[:height, :width]\n    box_masks = (\n        (x[None, :] >= bboxes[:, 0, None, None])\n        & (x[None, :] <= bboxes[:, 2, None, None])\n        & (y[None, :] >= bboxes[:, 1, None, None])\n        & (y[None, :] <= bboxes[:, 3, None, None])\n    )\n\n    # Calculate the area of each bounding box\n    box_areas = (bboxes[:, 2] - bboxes[:, 0]) * (bboxes[:, 3] - bboxes[:, 1])\n\n    # Calculate the visible area of each box (non-intersecting area with dropout mask)\n    visible_areas = np.sum(box_masks & ~dropout_mask.squeeze(), axis=(1, 2))\n\n    # Calculate visibility ratio (visible area / total box area)\n    visibility_ratio = visible_areas / box_areas\n\n    # Create a boolean mask for boxes to keep\n    keep_mask = (visible_areas >= min_area) & (visibility_ratio >= min_visibility)\n\n    return bboxes[keep_mask]\n
"},{"location":"api_reference/augmentations/dropout/grid_dropout/","title":"GridDropout augmentation (augmentations.dropout.grid_dropout)","text":""},{"location":"api_reference/augmentations/dropout/grid_dropout/#albumentations.augmentations.dropout.grid_dropout.GridDropout","title":"class GridDropout (ratio=0.5, unit_size_min=None, unit_size_max=None, holes_number_x=None, holes_number_y=None, shift_x=None, shift_y=None, random_offset=True, fill_value=None, mask_fill_value=None, unit_size_range=None, holes_number_xy=None, shift_xy=(0, 0), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply GridDropout augmentation to images, masks, bounding boxes, and keypoints.

GridDropout drops out rectangular regions of an image and the corresponding mask in a grid fashion. This technique can help improve model robustness by forcing the network to rely on a broader context rather than specific local features.

Parameters:

Name Type Description ratio float

The ratio of the mask holes to the unit size (same for horizontal and vertical directions). Must be between 0 and 1. Default: 0.5.

unit_size_range tuple[int, int] | None

Range from which to sample grid size. Default: None. Must be between 2 and the image's shorter edge. If None, grid size is calculated based on image size.

holes_number_xy tuple[int, int] | None

The number of grid units in x and y directions. First value should be between 1 and image width//2, Second value should be between 1 and image height//2. Default: None. If provided, overrides unit_size_range.

random_offset bool

Whether to offset the grid randomly between 0 and (grid unit size - hole size). If True, entered shift_xy is ignored and set randomly. Default: True.

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

fill_mask ColorType | None

Value for the dropped pixels in mask. If None, the mask is not modified. Default: None.

shift_xy tuple[int, int]

Offsets of the grid start in x and y directions from (0,0) coordinate. Only used when random_offset is False. Default: (0, 0).

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If both unit_size_range and holes_number_xy are None, the grid size is calculated based on the image size.
  • The actual number of dropped regions may differ slightly from holes_number_xy due to rounding.
  • Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.
  • For 'random_uniform' fill, each grid cell gets a single random color, unlike 'random' where each pixel gets its own random value.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> # Example with standard fill value\n>>> aug_basic = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     random_offset=True,\n...     p=1.0\n... )\n>>> # Example with random uniform fill\n>>> aug_random = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     fill=\"random_uniform\",\n...     p=1.0\n... )\n>>> # Example with inpainting\n>>> aug_inpaint = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     fill=\"inpaint_ns\",\n...     p=1.0\n... )\n>>> transformed = aug_random(image=image, mask=mask)\n>>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n

Reference

  • Paper: https://arxiv.org/abs/2001.04086
  • OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/grid_dropout.py Python
class GridDropout(BaseDropout):\n    \"\"\"Apply GridDropout augmentation to images, masks, bounding boxes, and keypoints.\n\n    GridDropout drops out rectangular regions of an image and the corresponding mask in a grid fashion.\n    This technique can help improve model robustness by forcing the network to rely on a broader context\n    rather than specific local features.\n\n    Args:\n        ratio (float): The ratio of the mask holes to the unit size (same for horizontal and vertical directions).\n            Must be between 0 and 1. Default: 0.5.\n        unit_size_range (tuple[int, int] | None): Range from which to sample grid size. Default: None.\n            Must be between 2 and the image's shorter edge. If None, grid size is calculated based on image size.\n        holes_number_xy (tuple[int, int] | None): The number of grid units in x and y directions.\n            First value should be between 1 and image width//2,\n            Second value should be between 1 and image height//2.\n            Default: None. If provided, overrides unit_size_range.\n        random_offset (bool): Whether to offset the grid randomly between 0 and (grid unit size - hole size).\n            If True, entered shift_xy is ignored and set randomly. Default: True.\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        fill_mask (ColorType | None): Value for the dropped pixels in mask.\n            If None, the mask is not modified. Default: None.\n        shift_xy (tuple[int, int]): Offsets of the grid start in x and y directions from (0,0) coordinate.\n            Only used when random_offset is False. Default: (0, 0).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If both unit_size_range and holes_number_xy are None, the grid size is calculated based on the image size.\n        - The actual number of dropped regions may differ slightly from holes_number_xy due to rounding.\n        - Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.\n        - For 'random_uniform' fill, each grid cell gets a single random color, unlike 'random' where each pixel\n            gets its own random value.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> # Example with standard fill value\n        >>> aug_basic = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     random_offset=True,\n        ...     p=1.0\n        ... )\n        >>> # Example with random uniform fill\n        >>> aug_random = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     fill=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> # Example with inpainting\n        >>> aug_inpaint = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     fill=\"inpaint_ns\",\n        ...     p=1.0\n        ... )\n        >>> transformed = aug_random(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n\n    Reference:\n        - Paper: https://arxiv.org/abs/2001.04086\n        - OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        ratio: float = Field(gt=0, le=1)\n\n        unit_size_min: int | None = Field(ge=2)\n        unit_size_max: int | None = Field(ge=2)\n\n        holes_number_x: int | None = Field(ge=1)\n        holes_number_y: int | None = Field(ge=1)\n\n        shift_x: int | None = Field(ge=0)\n        shift_y: int | None = Field(ge=0)\n\n        random_offset: bool\n        fill_value: DropoutFillValue | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_fill_value: ColorType | None = Field(deprecated=\"Deprecated use fill_mask instead\")\n\n        unit_size_range: (\n            Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None)), AfterValidator(nondecreasing)]\n            | None\n        )\n        shift_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(0, None))]\n\n        holes_number_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))] | None\n\n        @model_validator(mode=\"after\")\n        def validate_normalization(self) -> Self:\n            if self.unit_size_min is not None and self.unit_size_max is not None:\n                self.unit_size_range = self.unit_size_min, self.unit_size_max\n                warn(\n                    \"unit_size_min and unit_size_max are deprecated. Use unit_size_range instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.shift_x is not None and self.shift_y is not None:\n                self.shift_xy = self.shift_x, self.shift_y\n                warn(\"shift_x and shift_y are deprecated. Use shift_xy instead.\", DeprecationWarning, stacklevel=2)\n\n            if self.holes_number_x is not None and self.holes_number_y is not None:\n                self.holes_number_xy = self.holes_number_x, self.holes_number_y\n                warn(\n                    \"holes_number_x and holes_number_y are deprecated. Use holes_number_xy instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.unit_size_range and not MIN_UNIT_SIZE <= self.unit_size_range[0] <= self.unit_size_range[1]:\n                raise ValueError(\"Max unit size should be >= min size, both at least 2 pixels.\")\n\n            return self\n\n    def __init__(\n        self,\n        ratio: float = 0.5,\n        unit_size_min: int | None = None,\n        unit_size_max: int | None = None,\n        holes_number_x: int | None = None,\n        holes_number_y: int | None = None,\n        shift_x: int | None = None,\n        shift_y: int | None = None,\n        random_offset: bool = True,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        unit_size_range: tuple[int, int] | None = None,\n        holes_number_xy: tuple[int, int] | None = None,\n        shift_xy: tuple[int, int] = (0, 0),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.ratio = ratio\n        self.unit_size_range = unit_size_range\n        self.holes_number_xy = holes_number_xy\n        self.random_offset = random_offset\n        self.shift_xy = shift_xy\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image_shape = params[\"shape\"]\n        if self.holes_number_xy:\n            grid = self.holes_number_xy\n        else:\n            # Calculate grid based on unit_size_range or default\n            unit_height, unit_width = fdropout.calculate_grid_dimensions(\n                image_shape,\n                self.unit_size_range,\n                self.holes_number_xy,\n                self.random_generator,\n            )\n            grid = (image_shape[0] // unit_height, image_shape[1] // unit_width)\n\n        holes = fdropout.generate_grid_holes(\n            image_shape,\n            grid,\n            self.ratio,\n            self.random_offset,\n            self.shift_xy,\n            self.random_generator,\n        )\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"ratio\",\n            \"unit_size_range\",\n            \"holes_number_xy\",\n            \"shift_xy\",\n            \"random_offset\",\n        )\n
"},{"location":"api_reference/augmentations/dropout/mask_dropout/","title":"MaskDropout augmentation (augmentations.dropout.mask_dropout)","text":""},{"location":"api_reference/augmentations/dropout/mask_dropout/#albumentations.augmentations.dropout.mask_dropout.MaskDropout","title":"class MaskDropout (max_objects=(1, 1), image_fill_value=None, mask_fill_value=None, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply dropout to random objects in a mask, zeroing out the corresponding regions in both the image and mask.

This transform identifies objects in the mask (where each unique non-zero value represents a distinct object), randomly selects a number of these objects, and sets their corresponding regions to zero in both the image and mask. It can also handle bounding boxes and keypoints, removing or adjusting them based on the dropout regions.

Parameters:

Name Type Description max_objects int | tuple[int, int]

Maximum number of objects to dropout. If a single int is provided, it's treated as the upper bound. If a tuple of two ints is provided, it's treated as a range [min, max].

fill float | str | Literal[\"inpaint\"]

Value to fill the dropped out regions in the image. If set to 'inpaint', it applies inpainting to the dropped out regions (works only for 3-channel images).

fill_mask float | int

Value to fill the dropped out regions in the mask.

min_area float

Minimum area (in pixels) of a bounding box that must remain visible after dropout to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0

min_visibility float

Minimum visibility ratio (visible area / total area) of a bounding box after dropout to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The mask should be a single-channel image where 0 represents the background and non-zero values represent different object instances.
  • For bounding box and keypoint augmentation, make sure to set up the corresponding processors in the pipeline.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n>>> # Define a sample image, mask, and bounding boxes\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.zeros((100, 100), dtype=np.uint8)\n>>> mask[20:40, 20:40] = 1  # Object 1\n>>> mask[60:80, 60:80] = 2  # Object 2\n>>> bboxes = np.array([[20, 20, 40, 40], [60, 60, 80, 80]])\n>>>\n>>> # Define the transform\n>>> transform = A.Compose([\n...     A.MaskDropout(max_objects=1, mask_fill_value=0, min_area=100, min_visibility=0.5, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', min_area=1, min_visibility=0.1))\n>>>\n>>> # Apply the transform\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes)\n>>>\n>>> # The result will have one of the objects dropped out in both image and mask,\n>>> # and the corresponding bounding box removed if it doesn't meet the area and visibility criteria\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/mask_dropout.py Python
class MaskDropout(DualTransform):\n    \"\"\"Apply dropout to random objects in a mask, zeroing out the corresponding regions in both the image and mask.\n\n    This transform identifies objects in the mask (where each unique non-zero value represents a distinct object),\n    randomly selects a number of these objects, and sets their corresponding regions to zero in both the image and mask.\n    It can also handle bounding boxes and keypoints, removing or adjusting them based on the dropout regions.\n\n    Args:\n        max_objects (int | tuple[int, int]): Maximum number of objects to dropout. If a single int is provided,\n            it's treated as the upper bound. If a tuple of two ints is provided, it's treated as a range [min, max].\n        fill (float | str | Literal[\"inpaint\"]): Value to fill the dropped out regions in the image.\n            If set to 'inpaint', it applies inpainting to the dropped out regions (works only for 3-channel images).\n        fill_mask (float | int): Value to fill the dropped out regions in the mask.\n        min_area (float): Minimum area (in pixels) of a bounding box that must remain visible after dropout to be kept.\n            Only applicable if bounding box augmentation is enabled. Default: 0.0\n        min_visibility (float): Minimum visibility ratio (visible area / total area) of a bounding box after dropout\n            to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The mask should be a single-channel image where 0 represents the background and non-zero values represent\n          different object instances.\n        - For bounding box and keypoint augmentation, make sure to set up the corresponding processors in the pipeline.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        >>> # Define a sample image, mask, and bounding boxes\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.zeros((100, 100), dtype=np.uint8)\n        >>> mask[20:40, 20:40] = 1  # Object 1\n        >>> mask[60:80, 60:80] = 2  # Object 2\n        >>> bboxes = np.array([[20, 20, 40, 40], [60, 60, 80, 80]])\n        >>>\n        >>> # Define the transform\n        >>> transform = A.Compose([\n        ...     A.MaskDropout(max_objects=1, mask_fill_value=0, min_area=100, min_visibility=0.5, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', min_area=1, min_visibility=0.1))\n        >>>\n        >>> # Apply the transform\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes)\n        >>>\n        >>> # The result will have one of the objects dropped out in both image and mask,\n        >>> # and the corresponding bounding box removed if it doesn't meet the area and visibility criteria\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_objects: OnePlusIntRangeType\n\n        image_fill_value: float | Literal[\"inpaint\"] | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_fill_value: float | None = Field(deprecated=\"Deprecated use fill_mask instead\")\n\n        fill: float | Literal[\"inpaint\"]\n        fill_mask: float\n\n    def __init__(\n        self,\n        max_objects: ScaleIntType = (1, 1),\n        image_fill_value: float | Literal[\"inpaint\"] | None = None,\n        mask_fill_value: float | None = None,\n        fill: float | Literal[\"inpaint\"] = 0,\n        fill_mask: float = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_objects = cast(tuple[int, int], max_objects)\n        self.fill = fill  # type: ignore[assignment]\n        self.fill_mask = fill_mask\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [\"mask\"]\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        mask = data[\"mask\"]\n\n        label_image, num_labels = fdropout.label(mask, return_num=True)\n\n        if num_labels == 0:\n            dropout_mask = None\n        else:\n            objects_to_drop = self.py_random.randint(*self.max_objects)\n            objects_to_drop = min(num_labels, objects_to_drop)\n\n            if objects_to_drop == num_labels:\n                dropout_mask = mask > 0\n            else:\n                labels_index = self.py_random.sample(range(1, num_labels + 1), objects_to_drop)\n                dropout_mask = np.zeros(mask.shape[:2], dtype=bool)\n                for label_index in labels_index:\n                    dropout_mask |= label_image == label_index\n\n        return {\"dropout_mask\": dropout_mask}\n\n    def apply(self, img: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return img\n\n        if self.fill == \"inpaint\":\n            dropout_mask = dropout_mask.astype(np.uint8)\n            _, _, width, height = cv2.boundingRect(dropout_mask)\n            radius = min(3, max(width, height) // 2)\n            return cv2.inpaint(img, dropout_mask, radius, cv2.INPAINT_NS)\n\n        img = img.copy()\n        img[dropout_mask] = self.fill\n\n        return img\n\n    def apply_to_mask(self, mask: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return mask\n\n        mask = mask.copy()\n        mask[dropout_mask] = self.fill_mask\n        return mask\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return bboxes\n\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n\n        image_shape = params[\"shape\"][:2]\n\n        denormalized_bboxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fdropout.mask_dropout_bboxes(\n            denormalized_bboxes,\n            dropout_mask,\n            image_shape,\n            processor.params.min_area,\n            processor.params.min_visibility,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return keypoints\n\n        processor = cast(KeypointsProcessor, self.get_processor(\"keypoints\"))\n\n        if processor is None or not processor.params.remove_invisible:\n            return keypoints\n\n        return fdropout.mask_dropout_keypoints(keypoints, dropout_mask)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_objects\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/dropout/xy_masking/","title":"XYMasking augmentation (augmentations.dropout.xy_masking)","text":""},{"location":"api_reference/augmentations/dropout/xy_masking/#albumentations.augmentations.dropout.xy_masking.XYMasking","title":"class XYMasking (num_masks_x=0, num_masks_y=0, mask_x_length=0, mask_y_length=0, fill_value=None, mask_fill_value=None, fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Applies masking strips to an image, either horizontally (X axis) or vertically (Y axis), simulating occlusions. This transform is useful for training models to recognize images with varied visibility conditions. It's particularly effective for spectrogram images, allowing spectral and frequency masking to improve model robustness.

At least one of max_x_length or max_y_length must be specified, dictating the mask's maximum size along each axis.

Parameters:

Name Type Description num_masks_x int | tuple[int, int]

Number or range of horizontal regions to mask. Defaults to 0.

num_masks_y int | tuple[int, int]

Number or range of vertical regions to mask. Defaults to 0.

mask_x_length int | tuple[int, int]

Specifies the length of the masks along the X (horizontal) axis. If an integer is provided, it sets a fixed mask length. If a tuple of two integers (min, max) is provided, the mask length is randomly chosen within this range for each mask. This allows for variable-length masks in the horizontal direction.

mask_y_length int | tuple[int, int]

Specifies the height of the masks along the Y (vertical) axis. Similar to mask_x_length, an integer sets a fixed mask height, while a tuple (min, max) allows for variable-height masks, chosen randomly within the specified range for each mask. This flexibility facilitates creating masks of various sizes in the vertical direction.

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

mask_fill_value ColorType | None

Fill value for dropout regions in the mask. If None, mask regions corresponding to image dropouts are unchanged. Default: None

p float

Probability of applying the transform. Defaults to 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note: Either max_x_length or max_y_length or both must be defined.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/xy_masking.py Python
class XYMasking(BaseDropout):\n    \"\"\"Applies masking strips to an image, either horizontally (X axis) or vertically (Y axis),\n    simulating occlusions. This transform is useful for training models to recognize images\n    with varied visibility conditions. It's particularly effective for spectrogram images,\n    allowing spectral and frequency masking to improve model robustness.\n\n    At least one of `max_x_length` or `max_y_length` must be specified, dictating the mask's\n    maximum size along each axis.\n\n    Args:\n        num_masks_x (int | tuple[int, int]): Number or range of horizontal regions to mask. Defaults to 0.\n        num_masks_y (int | tuple[int, int]): Number or range of vertical regions to mask. Defaults to 0.\n        mask_x_length (int | tuple[int, int]): Specifies the length of the masks along\n            the X (horizontal) axis. If an integer is provided, it sets a fixed mask length.\n            If a tuple of two integers (min, max) is provided,\n            the mask length is randomly chosen within this range for each mask.\n            This allows for variable-length masks in the horizontal direction.\n        mask_y_length (int | tuple[int, int]): Specifies the height of the masks along\n            the Y (vertical) axis. Similar to `mask_x_length`, an integer sets a fixed mask height,\n            while a tuple (min, max) allows for variable-height masks, chosen randomly\n            within the specified range for each mask. This flexibility facilitates creating masks of various\n            sizes in the vertical direction.\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        mask_fill_value (ColorType | None): Fill value for dropout regions in the mask.\n            If None, mask regions corresponding to image dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Defaults to 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note: Either `max_x_length` or `max_y_length` or both must be defined.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_masks_x: NonNegativeIntRangeType\n        num_masks_y: NonNegativeIntRangeType\n        mask_x_length: NonNegativeIntRangeType\n        mask_y_length: NonNegativeIntRangeType\n\n        fill_value: DropoutFillValue | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_fill_value: ColorType | None = Field(deprecated=\"Deprecated use fill_mask instead\")\n\n        fill: DropoutFillValue\n        fill_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def check_mask_length(self) -> Self:\n            if (\n                isinstance(self.mask_x_length, int)\n                and self.mask_x_length <= 0\n                and isinstance(self.mask_y_length, int)\n                and self.mask_y_length <= 0\n            ):\n                msg = \"At least one of `mask_x_length` or `mask_y_length` Should be a positive number.\"\n                raise ValueError(msg)\n\n            if self.fill_value is not None:\n                self.fill = self.fill_value\n\n            if self.mask_fill_value is not None:\n                self.fill_mask = self.mask_fill_value\n\n            return self\n\n    def __init__(\n        self,\n        num_masks_x: ScaleIntType = 0,\n        num_masks_y: ScaleIntType = 0,\n        mask_x_length: ScaleIntType = 0,\n        mask_y_length: ScaleIntType = 0,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, fill=fill, fill_mask=fill_mask)\n        self.num_masks_x = cast(tuple[int, int], num_masks_x)\n        self.num_masks_y = cast(tuple[int, int], num_masks_y)\n\n        self.mask_x_length = cast(tuple[int, int], mask_x_length)\n        self.mask_y_length = cast(tuple[int, int], mask_y_length)\n\n    def validate_mask_length(\n        self,\n        mask_length: tuple[int, int] | None,\n        dimension_size: int,\n        dimension_name: str,\n    ) -> None:\n        \"\"\"Validate the mask length against the corresponding image dimension size.\"\"\"\n        if mask_length is not None:\n            if isinstance(mask_length, (tuple, list)):\n                if mask_length[0] < 0 or mask_length[1] > dimension_size:\n                    raise ValueError(\n                        f\"{dimension_name} range {mask_length} is out of valid range [0, {dimension_size}]\",\n                    )\n            elif mask_length < 0 or mask_length > dimension_size:\n                raise ValueError(f\"{dimension_name} {mask_length} exceeds image {dimension_name} {dimension_size}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        height, width = image_shape\n\n        self.validate_mask_length(self.mask_x_length, width, \"mask_x_length\")\n        self.validate_mask_length(self.mask_y_length, height, \"mask_y_length\")\n\n        masks_x = self.generate_masks(self.num_masks_x, image_shape, self.mask_x_length, axis=\"x\")\n        masks_y = self.generate_masks(self.num_masks_y, image_shape, self.mask_y_length, axis=\"y\")\n\n        holes = np.array(masks_x + masks_y)\n\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def generate_mask_size(self, mask_length: tuple[int, int]) -> int:\n        return self.py_random.randint(*mask_length)\n\n    def generate_masks(\n        self,\n        num_masks: tuple[int, int],\n        image_shape: tuple[int, int],\n        max_length: tuple[int, int] | None,\n        axis: str,\n    ) -> list[tuple[int, int, int, int]]:\n        if max_length is None or max_length == 0 or (isinstance(num_masks, (int, float)) and num_masks == 0):\n            return []\n\n        masks = []\n        num_masks_integer = (\n            num_masks if isinstance(num_masks, int) else self.py_random.randint(num_masks[0], num_masks[1])\n        )\n\n        height, width = image_shape\n\n        for _ in range(num_masks_integer):\n            length = self.generate_mask_size(max_length)\n\n            if axis == \"x\":\n                x_min = self.py_random.randint(0, width - length)\n                y_min = 0\n                x_max, y_max = x_min + length, height\n            else:  # axis == 'y'\n                y_min = self.py_random.randint(0, height - length)\n                x_min = 0\n                x_max, y_max = width, y_min + length\n\n            masks.append((x_min, y_min, x_max, y_max))\n        return masks\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"num_masks_x\",\n            \"num_masks_y\",\n            \"mask_x_length\",\n            \"mask_y_length\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/","title":"Index","text":"
  • Geometric functional transforms (albumentations.augmentations.geometric.functional)
  • Resizing transforms (augmentations.geometric.resize)
  • Rotation transforms (augmentations.geometric.functional)
  • Geometric transforms (augmentations.geometric.transforms)
"},{"location":"api_reference/augmentations/geometric/functional/","title":"Geometric functional transforms (augmentations.geometric.functional)","text":""},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.adjust_padding_by_position","title":"def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py Python
def adjust_padding_by_position(\n    h_top: int,\n    h_bottom: int,\n    w_left: int,\n    w_right: int,\n    position: PositionType,\n    py_random: np.random.RandomState,\n) -> tuple[int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position.\"\"\"\n    if position == \"center\":\n        return h_top, h_bottom, w_left, w_right\n\n    if position == \"top_left\":\n        return 0, h_top + h_bottom, 0, w_left + w_right\n\n    if position == \"top_right\":\n        return 0, h_top + h_bottom, w_left + w_right, 0\n\n    if position == \"bottom_left\":\n        return h_top + h_bottom, 0, 0, w_left + w_right\n\n    if position == \"bottom_right\":\n        return h_top + h_bottom, 0, w_left + w_right, 0\n\n    if position == \"random\":\n        h_pad = h_top + h_bottom\n        w_pad = w_left + w_right\n        h_top = py_random.randint(0, h_pad)\n        h_bottom = h_pad - h_top\n        w_left = py_random.randint(0, w_pad)\n        w_right = w_pad - w_left\n        return h_top, h_bottom, w_left, w_right\n\n    raise ValueError(f\"Unknown position: {position}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.almost_equal_intervals","title":"def almost_equal_intervals (n, parts) [view source on GitHub]","text":"

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)\narray([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n>>> almost_equal_intervals(16, 4)\narray([4, 4, 4, 4])  # Splits 16 into four equal parts\n
Source code in albumentations/augmentations/geometric/functional.py Python
def almost_equal_intervals(n: int, parts: int) -> np.ndarray:\n    \"\"\"Generates an array of nearly equal integer intervals that sum up to `n`.\n\n    This function divides the number `n` into `parts` nearly equal parts. It ensures that\n    the sum of all parts equals `n`, and the difference between any two parts is at most one.\n    This is useful for distributing a total amount into nearly equal discrete parts.\n\n    Args:\n        n (int): The total value to be split.\n        parts (int): The number of parts to split into.\n\n    Returns:\n        np.ndarray: An array of integers where each integer represents the size of a part.\n\n    Example:\n        >>> almost_equal_intervals(20, 3)\n        array([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n        >>> almost_equal_intervals(16, 4)\n        array([4, 4, 4, 4])  # Splits 16 into four equal parts\n    \"\"\"\n    part_size, remainder = divmod(n, parts)\n    # Create an array with the base part size and adjust the first `remainder` parts by adding 1\n    return np.array(\n        [part_size + 1 if i < remainder else part_size for i in range(parts)],\n    )\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.apply_affine_to_points","title":"def apply_affine_to_points (points, matrix) [view source on GitHub]","text":"

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"points\")\ndef apply_affine_to_points(points: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply affine transformation to a set of points.\n\n    This function handles potential division by zero by replacing zero values\n    in the homogeneous coordinate with a small epsilon value.\n\n    Args:\n        points (np.ndarray): Array of points with shape (N, 2).\n        matrix (np.ndarray): 3x3 affine transformation matrix.\n\n    Returns:\n        np.ndarray: Transformed points with shape (N, 2).\n    \"\"\"\n    homogeneous_points = np.column_stack([points, np.ones(points.shape[0])])\n    transformed_points = homogeneous_points @ matrix.T\n\n    # Handle potential division by zero\n    epsilon = np.finfo(transformed_points.dtype).eps\n    transformed_points[:, 2] = np.where(\n        np.abs(transformed_points[:, 2]) < epsilon,\n        np.sign(transformed_points[:, 2]) * epsilon,\n        transformed_points[:, 2],\n    )\n\n    return transformed_points[:, :2] / transformed_points[:, 2:]\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine","title":"def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine(\n    bboxes: np.ndarray,\n    matrix: np.ndarray,\n    rotate_method: Literal[\"largest_box\", \"ellipse\"],\n    image_shape: tuple[int, int],\n    border_mode: int,\n    output_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes.\n\n    For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function:\n    1. Calculates necessary padding to avoid information loss\n    2. Applies padding to the bounding boxes\n    3. Adjusts the transformation matrix to account for padding\n    4. Applies the affine transformation\n    5. Validates the transformed bounding boxes\n\n    For other border modes, it directly applies the affine transformation without padding.\n\n    Args:\n        bboxes (np.ndarray): Input bounding boxes\n        matrix (np.ndarray): Affine transformation matrix\n        rotate_method (str): Method for rotating bounding boxes ('largest_box' or 'ellipse')\n        image_shape (Sequence[int]): Shape of the input image\n        border_mode (int): OpenCV border mode\n        output_shape (Sequence[int]): Shape of the output image\n\n    Returns:\n        np.ndarray: Transformed and normalized bounding boxes\n    \"\"\"\n    if is_identity_matrix(matrix):\n        return bboxes\n\n    bboxes = denormalize_bboxes(bboxes, image_shape)\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        bboxes = generate_reflected_bboxes(\n            bboxes,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Apply affine transform\n    if rotate_method == \"largest_box\":\n        transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n    elif rotate_method == \"ellipse\":\n        transformed_bboxes = bboxes_affine_ellipse(bboxes, matrix)\n    else:\n        raise ValueError(f\"Method {rotate_method} is not a valid rotation method.\")\n\n    # Validate and normalize bboxes\n    validated_bboxes = validate_bboxes(transformed_bboxes, output_shape)\n\n    return normalize_bboxes(validated_bboxes, output_shape)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine_ellipse","title":"def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_ellipse(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes using an ellipse approximation method.\n\n    This function transforms bounding boxes by approximating each box with an ellipse,\n    transforming points along the ellipse's circumference, and then computing the\n    new bounding box that encloses the transformed ellipse.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The ellipse approximation method can provide a tighter bounding box compared to the\n          largest box method, especially for rotations.\n        - 360 points are used to approximate each ellipse, which provides a good balance between\n          accuracy and computational efficiency.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method may be more suitable for objects that are roughly elliptical in shape.\n    \"\"\"\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n    bbox_width = (x_max - x_min) / 2\n    bbox_height = (y_max - y_min) / 2\n    center_x = x_min + bbox_width\n    center_y = y_min + bbox_height\n\n    angles = np.arange(0, 360, dtype=np.float32)\n    cos_angles = np.cos(np.radians(angles))\n    sin_angles = np.sin(np.radians(angles))\n\n    # Generate points for all ellipses at once\n    x = bbox_width[:, np.newaxis] * sin_angles + center_x[:, np.newaxis]\n    y = bbox_height[:, np.newaxis] * cos_angles + center_y[:, np.newaxis]\n    points = np.stack([x, y], axis=-1).reshape(-1, 2)\n\n    # Transform all points at once using the helper function\n    transformed_points = apply_affine_to_points(points, matrix)\n\n    transformed_points = transformed_points.reshape(len(bboxes), -1, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_points[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_points[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_points[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_points[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine_largest_box","title":"def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called \"largest box\" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n>>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n>>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n>>> print(transformed_bboxes)\n[[ 25.  25.  45.  45.   1.]\n [ 65.  65.  85.  85.   2.]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_largest_box(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes and return the largest enclosing boxes.\n\n    This function transforms each corner of every bounding box using the given affine transformation\n    matrix, then computes the new bounding boxes that fully enclose the transformed corners.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The resulting bounding boxes are the smallest axis-aligned boxes that completely\n          enclose the transformed original boxes. They may be larger than the minimal possible\n          bounding box if the original box becomes rotated.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method is called \"largest box\" because it returns the largest axis-aligned box\n          that encloses all corners of the transformed bounding box.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n        >>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n        >>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n        >>> print(transformed_bboxes)\n        [[ 25.  25.  45.  45.   1.]\n         [ 65.  65.  85.  85.   2.]]\n    \"\"\"\n    # Extract corners of all bboxes\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    corners = (\n        np.array([[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]]).transpose(2, 0, 1).reshape(-1, 2)\n    )\n\n    # Transform all corners at once\n    transformed_corners = apply_affine_to_points(corners, matrix).reshape(-1, 4, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_corners[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_corners[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_corners[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_corners[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_d4","title":"def bboxes_d4 (bboxes, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_d4(\n    bboxes: np.ndarray,\n    group_member: D4Type,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a bounding box.\n\n    The function transforms a bounding box according to the specified group member from the `D_4` group.\n    These transformations include rotations and reflections, specified to work on an image's bounding box given\n    its dimensions.\n\n    Parameters:\n    -  bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n    - group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.\n\n    Returns:\n    - BoxInternalType: The transformed bounding box.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Applying a 90-degree rotation:\n      `bbox_d4((10, 20, 110, 120), 'r90')`\n      This would rotate the bounding box 90 degrees within a 100x100 image.\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: bboxes_rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: bboxes_rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: bboxes_rot90(x, 3),  # Rotate 270 degrees\n        \"v\": lambda x: bboxes_vflip(x),  # Vertical flip\n        \"hvt\": lambda x: bboxes_transpose(\n            bboxes_rot90(x, 2),\n        ),  # Reflect over anti-diagonal\n        \"h\": lambda x: bboxes_hflip(x),  # Horizontal flip\n        \"t\": lambda x: bboxes_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](bboxes)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_grid_shuffle","title":"def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n>>> tiles = np.array([\n...     [0, 0, 50, 50],    # top-left tile\n...     [0, 50, 50, 100],  # top-right tile\n...     [50, 0, 100, 50],  # bottom-left tile\n...     [50, 50, 100, 100] # bottom-right tile\n... ])\n>>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n>>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n>>> # Result may contain multiple boxes if the original box was split\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_grid_shuffle(\n    bboxes: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int],\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Apply grid shuffle transformation to bounding boxes.\n\n    This function transforms bounding boxes according to a grid shuffle operation. It handles cases\n    where bounding boxes may be split into multiple components after shuffling and applies\n    filtering based on minimum area and visibility requirements.\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4+) where N is the number of boxes.\n               Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents\n               optional additional fields (e.g., class_id, score).\n        tiles: Array of tile coordinates with shape (M, 4) where M is the number of tiles.\n               Each tile is in format [start_y, start_x, end_y, end_x].\n        mapping: List of indices defining how tiles should be rearranged. Each index i in the list\n                contains the index of the tile that should be moved to position i.\n        image_shape: Shape of the image as (height, width).\n        min_area: Minimum area threshold in pixels. If a component's area after shuffling is\n                 smaller than this value, it will be filtered out. If None, no area filtering\n                 is applied.\n        min_visibility: Minimum visibility ratio threshold in range [0, 1]. Calculated as\n                       (component_area / original_area). If a component's visibility is lower\n                       than this value, it will be filtered out. If None, no visibility\n                       filtering is applied.\n\n    Returns:\n        np.ndarray: Array of transformed bounding boxes with shape (K, 4+) where K is the\n                   number of valid components after shuffling and filtering. The format of\n                   each box matches the input format, preserving any additional fields.\n                   If no valid components remain after filtering, returns an empty array\n                   with shape (0, C) where C matches the input column count.\n\n    Note:\n        - The function converts bboxes to masks before applying the transformation to handle\n          cases where boxes may be split into multiple components.\n        - After shuffling, each component is validated against min_area and min_visibility\n          requirements independently.\n        - Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and\n          copied to all components derived from the same original bbox.\n        - Empty input arrays are handled gracefully and return empty arrays of the\n          appropriate shape.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n        >>> tiles = np.array([\n        ...     [0, 0, 50, 50],    # top-left tile\n        ...     [0, 50, 50, 100],  # top-right tile\n        ...     [50, 0, 100, 50],  # bottom-left tile\n        ...     [50, 50, 100, 100] # bottom-right tile\n        ... ])\n        >>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n        >>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n        >>> # Result may contain multiple boxes if the original box was split\n    \"\"\"\n    # Convert bboxes to masks\n    masks = masks_from_bboxes(bboxes, image_shape)\n\n    # Apply grid shuffle to each mask and handle split components\n    all_component_masks = []\n    extra_bbox_data = []  # Store additional bbox data for each component\n\n    for idx, mask in enumerate(masks):\n        original_area = np.sum(mask)  # Get original mask area\n\n        # Shuffle the mask\n        shuffled_mask = swap_tiles_on_image(mask, tiles, mapping)\n\n        # Find connected components\n        num_components, components = cv2.connectedComponents(\n            shuffled_mask.astype(np.uint8),\n        )\n\n        # For each component, create a separate binary mask\n        for comp_idx in range(1, num_components):  # Skip background (0)\n            component_mask = (components == comp_idx).astype(np.uint8)\n\n            # Calculate area and visibility ratio\n            component_area = np.sum(component_mask)\n            # Check if component meets minimum requirements\n            if is_valid_component(\n                component_area,\n                original_area,\n                min_area,\n                min_visibility,\n            ):\n                all_component_masks.append(component_mask)\n                # Append additional bbox data for this component\n                if bboxes.shape[1] > NUM_BBOXES_COLUMNS_IN_ALBUMENTATIONS:\n                    extra_bbox_data.append(bboxes[idx, 4:])\n\n    # Convert all component masks to bboxes\n    if all_component_masks:\n        all_component_masks = np.array(all_component_masks)\n        shuffled_bboxes = bboxes_from_masks(all_component_masks)\n\n        # Add back additional bbox data if present\n        if extra_bbox_data:\n            extra_bbox_data = np.array(extra_bbox_data)\n            return np.column_stack([shuffled_bboxes, extra_bbox_data])\n    else:\n        # Handle case where no valid components were found\n        return np.zeros((0, bboxes.shape[1]), dtype=bboxes.dtype)\n\n    return shuffled_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_hflip","title":"def bboxes_hflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_hflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally around the y-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of horizontally flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 0] = 1 - bboxes[:, 2]  # new x_min = 1 - x_max\n    flipped_bboxes[:, 2] = 1 - bboxes[:, 0]  # new x_max = 1 - x_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_rot90","title":"def bboxes_rot90 (bboxes, factor) [view source on GitHub]","text":"

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor int

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Exceptions:

Type Description ValueError

If factor is not in set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_rot90(bboxes: np.ndarray, factor: int) -> np.ndarray:\n    \"\"\"Rotates bounding boxes by 90 degrees CCW (see np.rot90)\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n        factor: Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.\n\n    Returns:\n        np.ndarray: A numpy array of rotated bounding boxes with the same shape as input.\n\n    Raises:\n        ValueError: If factor is not in set {0, 1, 2, 3}.\n    \"\"\"\n    if factor not in {0, 1, 2, 3}:\n        raise ValueError(\"Parameter factor must be in set {0, 1, 2, 3}\")\n\n    if factor == 0:\n        return bboxes\n\n    rotated_bboxes = bboxes.copy()\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    if factor == 1:\n        rotated_bboxes[:, 0] = y_min\n        rotated_bboxes[:, 1] = 1 - x_max\n        rotated_bboxes[:, 2] = y_max\n        rotated_bboxes[:, 3] = 1 - x_min\n    elif factor == ROT90_180_FACTOR:\n        rotated_bboxes[:, 0] = 1 - x_max\n        rotated_bboxes[:, 1] = 1 - y_max\n        rotated_bboxes[:, 2] = 1 - x_min\n        rotated_bboxes[:, 3] = 1 - y_min\n    elif factor == ROT90_270_FACTOR:\n        rotated_bboxes[:, 0] = 1 - y_max\n        rotated_bboxes[:, 1] = x_min\n        rotated_bboxes[:, 2] = 1 - y_min\n        rotated_bboxes[:, 3] = x_max\n\n    return rotated_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_transpose","title":"def bboxes_transpose (bboxes) [view source on GitHub]","text":"

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_transpose(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Transpose bounding boxes by swapping x and y coordinates.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of transposed bounding boxes with the same shape as input.\n    \"\"\"\n    transposed_bboxes = bboxes.copy()\n    transposed_bboxes[:, [0, 1, 2, 3]] = bboxes[:, [1, 0, 3, 2]]\n\n    return transposed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_vflip","title":"def bboxes_vflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_vflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes vertically around the x-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of vertically flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 1] = 1 - bboxes[:, 3]  # new y_min = 1 - y_max\n    flipped_bboxes[:, 3] = 1 - bboxes[:, 1]  # new y_max = 1 - y_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.calculate_affine_transform_padding","title":"def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]","text":"

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py Python
def calculate_affine_transform_padding(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate the necessary padding for an affine transformation to avoid empty spaces.\"\"\"\n    height, width = image_shape[:2]\n\n    # Check for identity transform\n    if is_identity_matrix(matrix):\n        return (0, 0, 0, 0)\n\n    # Original corners\n    corners = np.array([[0, 0], [width, 0], [width, height], [0, height]])\n\n    # Transform corners\n    transformed_corners = apply_affine_to_points(corners, matrix)\n\n    # Ensure transformed_corners is 2D\n    transformed_corners = transformed_corners.reshape(-1, 2)\n\n    # Find box that includes both original and transformed corners\n    all_corners = np.vstack((corners, transformed_corners))\n    min_x, min_y = all_corners.min(axis=0)\n    max_x, max_y = all_corners.max(axis=0)\n\n    # Compute the inverse transform\n    inverse_matrix = np.linalg.inv(matrix)\n\n    # Apply inverse transform to all corners of the bounding box\n    bbox_corners = np.array(\n        [[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]],\n    )\n    inverse_corners = apply_affine_to_points(bbox_corners, inverse_matrix).reshape(\n        -1,\n        2,\n    )\n\n    min_x, min_y = inverse_corners.min(axis=0)\n    max_x, max_y = inverse_corners.max(axis=0)\n\n    pad_left = max(0, math.ceil(0 - min_x))\n    pad_right = max(0, math.ceil(max_x - width))\n    pad_top = max(0, math.ceil(0 - min_y))\n    pad_bottom = max(0, math.ceil(max_y - height))\n\n    return pad_left, pad_right, pad_top, pad_bottom\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.center","title":"def center (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates if image. Used by images, masks and keypoints.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2 - 0.5, height / 2 - 0.5\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.center_bbox","title":"def center_bbox (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates for of image for bounding boxes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2, height / 2\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.compute_tps_weights","title":"def compute_tps_weights (src_points, dst_points) [view source on GitHub]","text":"

Compute Thin Plate Spline weights.

Parameters:

Name Type Description src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_tps_weights(\n    src_points: np.ndarray,\n    dst_points: np.ndarray,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute Thin Plate Spline weights.\n\n    Args:\n        src_points: Source control points with shape (num_points, 2)\n        dst_points: Destination control points with shape (num_points, 2)\n\n    Returns:\n        tuple of:\n        - nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)\n        - affine_weights: Weights for affine transformation (3, 2)\n            [constant term, x scale/shear, y scale/shear]\n\n    Note:\n        The TPS interpolation is decomposed into:\n        1. Nonlinear part (controlled by kernel weights)\n        2. Affine part (global scaling, rotation, translation)\n    \"\"\"\n    num_points = src_points.shape[0]\n\n    # Compute pairwise distances\n    distances = np.linalg.norm(src_points[:, None] - src_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    # Add small epsilon to avoid log(0)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Construct affine terms matrix [1, x, y]\n    affine_terms = np.ones((num_points, 3))\n    affine_terms[:, 1:] = src_points\n\n    # Build system matrix\n    system_matrix = np.zeros((num_points + 3, num_points + 3))\n    system_matrix[:num_points, :num_points] = kernel_matrix\n    system_matrix[:num_points, num_points:] = affine_terms\n    system_matrix[num_points:, :num_points] = affine_terms.T\n\n    # Right-hand side of the system\n    target_coords = np.zeros((num_points + 3, 2))\n    target_coords[:num_points] = dst_points\n\n    # Solve the system for both x and y coordinates\n    all_weights = np.linalg.solve(system_matrix, target_coords)\n\n    # Split weights into nonlinear and affine components\n    nonlinear_weights = all_weights[:num_points]\n    affine_weights = all_weights[num_points:]\n\n    return nonlinear_weights, affine_weights\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.compute_transformed_image_bounds","title":"def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]","text":"

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_transformed_image_bounds(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute the bounds of an image after applying an affine transformation.\n\n    Args:\n        matrix (np.ndarray): The 3x3 affine transformation matrix.\n        image_shape (Tuple[int, int]): The shape of the image as (height, width).\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing:\n            - min_coords: An array with the minimum x and y coordinates.\n            - max_coords: An array with the maximum x and y coordinates.\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Define the corners of the image\n    corners = np.array([[0, 0, 1], [width, 0, 1], [width, height, 1], [0, height, 1]])\n\n    # Transform the corners\n    transformed_corners = corners @ matrix.T\n    transformed_corners = transformed_corners[:, :2] / transformed_corners[:, 2:]\n\n    # Calculate the bounding box of the transformed corners\n    min_coords = np.floor(transformed_corners.min(axis=0)).astype(int)\n    max_coords = np.ceil(transformed_corners.max(axis=0)).astype(int)\n\n    return min_coords, max_coords\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_affine_transformation_matrix","title":"def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]","text":"

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_affine_transformation_matrix(\n    translate: XYInt,\n    shear: XYFloat,\n    scale: XYFloat,\n    rotate: float,\n    shift: tuple[float, float],\n) -> np.ndarray:\n    \"\"\"Create an affine transformation matrix combining translation, shear, scale, and rotation.\n\n    Args:\n        translate (dict[str, float]): Translation in x and y directions.\n        shear (dict[str, float]): Shear in x and y directions (in degrees).\n        scale (dict[str, float]): Scale factors for x and y directions.\n        rotate (float): Rotation angle in degrees.\n        shift (tuple[float, float]): Shift to apply before and after transformations.\n\n    Returns:\n        np.ndarray: The resulting 3x3 affine transformation matrix.\n    \"\"\"\n    # Convert angles to radians\n    rotate_rad = np.deg2rad(rotate % 360)\n\n    shear_x_rad = np.deg2rad(shear[\"x\"])\n    shear_y_rad = np.deg2rad(shear[\"y\"])\n\n    # Create individual transformation matrices\n    # 1. Shift to top-left\n    m_shift_topleft = np.array([[1, 0, -shift[0]], [0, 1, -shift[1]], [0, 0, 1]])\n\n    # 2. Scale\n    m_scale = np.array([[scale[\"x\"], 0, 0], [0, scale[\"y\"], 0], [0, 0, 1]])\n\n    # 3. Rotation\n    m_rotate = np.array(\n        [\n            [np.cos(rotate_rad), np.sin(rotate_rad), 0],\n            [-np.sin(rotate_rad), np.cos(rotate_rad), 0],\n            [0, 0, 1],\n        ],\n    )\n\n    # 4. Shear\n    m_shear = np.array(\n        [[1, np.tan(shear_x_rad), 0], [np.tan(shear_y_rad), 1, 0], [0, 0, 1]],\n    )\n\n    # 5. Translation\n    m_translate = np.array([[1, 0, translate[\"x\"]], [0, 1, translate[\"y\"]], [0, 0, 1]])\n\n    # 6. Shift back to center\n    m_shift_center = np.array([[1, 0, shift[0]], [0, 1, shift[1]], [0, 0, 1]])\n\n    # Combine all transformations\n    # The order is important: transformations are applied from right to left\n    m = m_shift_center @ m_translate @ m_shear @ m_rotate @ m_scale @ m_shift_topleft\n\n    # Ensure the last row is exactly [0, 0, 1]\n    m[2] = [0, 0, 1]\n\n    return m\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_piecewise_affine_maps","title":"def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]","text":"

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_piecewise_affine_maps(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    scale: float,\n    absolute_scale: bool,\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray | None, np.ndarray | None]:\n    \"\"\"Create maps for piecewise affine transformation using OpenCV's remap function.\"\"\"\n    height, width = image_shape[:2]\n    nb_rows, nb_cols = grid\n\n    # Input validation\n    if height <= 0 or width <= 0 or nb_rows <= 0 or nb_cols <= 0:\n        raise ValueError(\"Dimensions must be positive\")\n    if scale <= 0:\n        return None, None\n\n    # Create source points grid\n    y = np.linspace(0, height - 1, nb_rows, dtype=np.float32)\n    x = np.linspace(0, width - 1, nb_cols, dtype=np.float32)\n    xx_src, yy_src = np.meshgrid(x, y)\n\n    # Initialize destination maps at full resolution\n    map_x = np.zeros((height, width), dtype=np.float32)\n    map_y = np.zeros((height, width), dtype=np.float32)\n\n    # Generate jitter for control points\n    jitter_scale = scale / 3 if absolute_scale else scale * min(width, height) / 3\n\n    jitter = random_generator.normal(0, jitter_scale, (nb_rows, nb_cols, 2)).astype(\n        np.float32,\n    )\n\n    # Create control points with jitter\n    control_points = np.zeros((nb_rows * nb_cols, 4), dtype=np.float32)\n    for i in range(nb_rows):\n        for j in range(nb_cols):\n            idx = i * nb_cols + j\n            # Source points\n            control_points[idx, 0] = xx_src[i, j]\n            control_points[idx, 1] = yy_src[i, j]\n            # Destination points with jitter\n            control_points[idx, 2] = np.clip(\n                xx_src[i, j] + jitter[i, j, 1],\n                0,\n                width - 1,\n            )\n            control_points[idx, 3] = np.clip(\n                yy_src[i, j] + jitter[i, j, 0],\n                0,\n                height - 1,\n            )\n\n    # Create full resolution maps\n    for i in range(height):\n        for j in range(width):\n            # Find nearest control points and interpolate\n            dx = j - control_points[:, 0]\n            dy = i - control_points[:, 1]\n            dist = dx * dx + dy * dy\n            weights = 1 / (dist + 1e-8)\n            weights = weights / np.sum(weights)\n\n            map_x[i, j] = np.sum(weights * control_points[:, 2])\n            map_y[i, j] = np.sum(weights * control_points[:, 3])\n\n    # Ensure output is within bounds\n    map_x = np.clip(map_x, 0, width - 1, out=map_x)\n    map_y = np.clip(map_y, 0, height - 1, out=map_y)\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_shape_groups","title":"def create_shape_groups (tiles) [view source on GitHub]","text":"

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:\n    \"\"\"Groups tiles by their shape and stores the indices for each shape.\"\"\"\n    shape_groups = defaultdict(list)\n    for index, (start_y, start_x, end_y, end_x) in enumerate(tiles):\n        shape = (end_y - start_y, end_x - start_x)\n        shape_groups[shape].append(index)\n    return shape_groups\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.d4","title":"def d4 (img, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to an image array.\n\n    This function manipulates an image using transformations such as rotations and flips,\n    corresponding to the `D_4` dihedral group symmetry operations.\n    Each transformation is identified by a unique group member code.\n\n    Parameters:\n    - img (np.ndarray): The input image array to transform.\n    - group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:\n      - 'e': Identity (no transformation).\n      - 'r90': Rotate 90 degrees counterclockwise.\n      - 'r180': Rotate 180 degrees.\n      - 'r270': Rotate 270 degrees counterclockwise.\n      - 'v': Vertical flip.\n      - 'hvt': Transpose over second diagonal\n      - 'h': Horizontal flip.\n      - 't': Transpose (reflect over the main diagonal).\n\n    Returns:\n    - np.ndarray: The transformed image array.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Rotating an image by 90 degrees:\n      `transformed_image = d4(original_image, 'r90')`\n    - Applying a horizontal flip to an image:\n      `transformed_image = d4(original_image, 'h')`\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: rot90(x, 3),  # Rotate 270 degrees\n        \"v\": vflip,  # Vertical flip\n        \"hvt\": lambda x: transpose(rot90(x, 2)),  # Reflect over anti-diagonal\n        \"h\": hflip,  # Horizontal flip\n        \"t\": transpose,  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](img)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.distort_image","title":"def distort_image (image, generated_mesh, interpolation) [view source on GitHub]","text":"

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n>>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n>>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n>>> distorted.shape\n(100, 100, 3)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@preserve_channel_dim\ndef distort_image(\n    image: np.ndarray,\n    generated_mesh: np.ndarray,\n    interpolation: int,\n) -> np.ndarray:\n    \"\"\"Apply perspective distortion to an image based on a generated mesh.\n\n    This function applies a perspective transformation to each cell of the image defined by the\n    generated mesh. The distortion is applied using OpenCV's perspective transformation and\n    blending techniques.\n\n    Args:\n        image (np.ndarray): The input image to be distorted. Can be a 2D grayscale image or a\n                            3D color image.\n        generated_mesh (np.ndarray): A 2D array where each row represents a quadrilateral cell\n                                    as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4].\n                                    The first four values define the source rectangle, and the last eight values\n                                    define the destination quadrilateral.\n        interpolation (int): Interpolation method to be used in the perspective transformation.\n                             Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).\n\n    Returns:\n        np.ndarray: The distorted image with the same shape and dtype as the input image.\n\n    Note:\n        - The function preserves the channel dimension of the input image.\n        - Each cell of the generated mesh is transformed independently and then blended into the output image.\n        - The distortion is applied using perspective transformation, which allows for more complex\n          distortions compared to affine transformations.\n\n    Example:\n        >>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n        >>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n        >>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n        >>> distorted.shape\n        (100, 100, 3)\n    \"\"\"\n    distorted_image = np.zeros_like(image)\n\n    for mesh in generated_mesh:\n        # Extract source rectangle and destination quadrilateral\n        x1, y1, x2, y2 = mesh[:4]  # Source rectangle\n        dst_quad = mesh[4:].reshape(4, 2)  # Destination quadrilateral\n\n        # Convert source rectangle to quadrilateral\n        src_quad = np.array(\n            [\n                [x1, y1],  # Top-left\n                [x2, y1],  # Top-right\n                [x2, y2],  # Bottom-right\n                [x1, y2],  # Bottom-left\n            ],\n            dtype=np.float32,\n        )\n\n        # Calculate Perspective transformation matrix\n        perspective_mat = cv2.getPerspectiveTransform(src_quad, dst_quad)\n\n        # Apply Perspective transformation\n        warped = cv2.warpPerspective(\n            image,\n            perspective_mat,\n            (image.shape[1], image.shape[0]),\n            flags=interpolation,\n        )\n\n        # Create mask for the transformed region\n        mask = np.zeros(image.shape[:2], dtype=np.uint8)\n        cv2.fillConvexPoly(mask, np.int32(dst_quad), 255)\n\n        # Copy only the warped quadrilateral area to the output image\n        distorted_image = cv2.copyTo(warped, mask, distorted_image)\n\n    return distorted_image\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.find_keypoint","title":"def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]","text":"

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py Python
def find_keypoint(\n    position: tuple[int, int],\n    distance_map: np.ndarray,\n    threshold: float | None,\n    inverted: bool,\n) -> tuple[float, float] | None:\n    \"\"\"Determine if a valid keypoint can be found at the given position.\"\"\"\n    y, x = position\n    value = distance_map[y, x]\n    if not inverted and threshold is not None and value >= threshold:\n        return None\n    if inverted and threshold is not None and value <= threshold:\n        return None\n    return float(x), float(y)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.flip_bboxes","title":"def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]","text":"

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef flip_bboxes(\n    bboxes: np.ndarray,\n    flip_horizontal: bool = False,\n    flip_vertical: bool = False,\n    image_shape: tuple[int, int] = (0, 0),\n) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally and/or vertically.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where each row is\n            [x_min, y_min, x_max, y_max, ...].\n        flip_horizontal (bool): Whether to flip horizontally.\n        flip_vertical (bool): Whether to flip vertically.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Flipped bounding boxes.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    flipped_bboxes = bboxes.copy()\n    if flip_horizontal:\n        flipped_bboxes[:, [0, 2]] = cols - flipped_bboxes[:, [2, 0]]\n    if flip_vertical:\n        flipped_bboxes[:, [1, 3]] = rows - flipped_bboxes[:, [3, 1]]\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.from_distance_maps","title":"def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]","text":"

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n>>> inverted = True\n>>> if_not_found_coords = [0, 0]\n>>> threshold = 0.5\n>>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n>>> print(keypoints.shape)\n(3, 2)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def from_distance_maps(\n    distance_maps: np.ndarray,\n    inverted: bool,\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None = None,\n    threshold: float | None = None,\n) -> np.ndarray:\n    \"\"\"Convert distance maps back to keypoints coordinates.\n\n    This function is the inverse of `to_distance_maps`. It takes distance maps generated for a set of keypoints\n    and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps,\n    and can handle cases where keypoints are not found or fall outside a specified threshold.\n\n    Args:\n        distance_maps (np.ndarray): A 3D numpy array of shape (height, width, nb_keypoints) containing\n            distance maps for each keypoint. Each channel represents the distance map for one keypoint.\n        inverted (bool): If True, treats the distance maps as inverted (where higher values indicate\n            closer proximity to keypoints). If False, treats them as regular distance maps (where lower\n            values indicate closer proximity).\n        if_not_found_coords (Sequence[int] | dict[str, Any] | None, optional): Coordinates to use for\n            keypoints that are not found or fall outside the threshold. Can be:\n            - None: Drop keypoints that are not found.\n            - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints.\n            - Dict with 'x' and 'y' keys: Use these values for not found keypoints.\n            Defaults to None.\n        threshold (float | None, optional): A threshold value to determine valid keypoints. For inverted\n            maps, values >= threshold are considered valid. For regular maps, values <= threshold are\n            considered valid. If None, all keypoints are considered valid. Defaults to None.\n\n    Returns:\n        np.ndarray: A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates\n        of the reconstructed keypoints. If `drop_if_not_found` is True (derived from if_not_found_coords),\n        the output may have fewer rows than input keypoints.\n\n    Raises:\n        ValueError: If the input `distance_maps` is not a 3D array.\n\n    Notes:\n        - The function uses vectorized operations for improved performance, especially with large numbers of keypoints.\n        - When `threshold` is None, all keypoints are considered valid, and `if_not_found_coords` is not used.\n        - The function assumes that the input distance maps are properly normalized and scaled according to the\n          original image dimensions.\n\n    Example:\n        >>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n        >>> inverted = True\n        >>> if_not_found_coords = [0, 0]\n        >>> threshold = 0.5\n        >>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n        >>> print(keypoints.shape)\n        (3, 2)\n    \"\"\"\n    if distance_maps.ndim != NUM_MULTI_CHANNEL_DIMENSIONS:\n        msg = f\"Expected three-dimensional input, got {distance_maps.ndim} dimensions and shape {distance_maps.shape}.\"\n        raise ValueError(msg)\n    height, width, nb_keypoints = distance_maps.shape\n\n    drop_if_not_found, if_not_found_x, if_not_found_y = validate_if_not_found_coords(\n        if_not_found_coords,\n    )\n\n    # Find the indices of max/min values for all keypoints at once\n    if inverted:\n        hitidx_flat = np.argmax(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n    else:\n        hitidx_flat = np.argmin(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n\n    # Convert flat indices to 2D coordinates\n    hitidx_y, hitidx_x = np.unravel_index(hitidx_flat, (height, width))\n\n    # Create keypoints array\n    keypoints = np.column_stack((hitidx_x, hitidx_y)).astype(float)\n\n    if threshold is not None:\n        # Check threshold condition\n        if inverted:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] >= threshold\n        else:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] <= threshold\n\n        if not drop_if_not_found:\n            # Replace invalid keypoints with if_not_found_coords\n            keypoints[~valid_mask] = [if_not_found_x, if_not_found_y]\n        else:\n            # Keep only valid keypoints\n            return keypoints[valid_mask]\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_displacement_fields","title":"def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]","text":"

Generate displacement fields for elastic transform.

Parameters:

Name Type Description image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use (\"gaussian\" or \"uniform\")

Returns:

Type Description tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_displacement_fields(\n    image_shape: tuple[int, int],\n    alpha: float,\n    sigma: float,\n    same_dxdy: bool,\n    kernel_size: tuple[int, int],\n    random_generator: np.random.Generator,\n    noise_distribution: Literal[\"gaussian\", \"uniform\"],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate displacement fields for elastic transform.\n\n    Args:\n        image_shape: Shape of the image (height, width)\n        alpha: Scaling factor for displacement\n        sigma: Standard deviation for Gaussian blur\n        same_dxdy: Whether to use same displacement field for both directions\n        kernel_size: Size of Gaussian blur kernel\n        random_generator: NumPy random number generator\n        noise_distribution: Type of noise distribution to use (\"gaussian\" or \"uniform\")\n\n    Returns:\n        tuple: (dx, dy) displacement fields\n    \"\"\"\n\n    def generate_noise_field() -> np.ndarray:\n        # Generate noise based on distribution type\n        if noise_distribution == \"gaussian\":\n            field = random_generator.standard_normal(size=image_shape[:2])\n        else:  # uniform\n            field = random_generator.uniform(low=-1, high=1, size=image_shape[:2])\n\n        # Common operations for both distributions\n        field = field.astype(np.float32)\n        cv2.GaussianBlur(field, kernel_size, sigma, dst=field)\n        return field * alpha\n\n    # Generate first displacement field\n    dx = generate_noise_field()\n\n    # Generate or copy second displacement field\n    dy = dx if same_dxdy else generate_noise_field()\n\n    return dx, dy\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_distorted_grid_polygons","title":"def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]","text":"

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n>>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n>>> distorted.shape\n(4, 8)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_distorted_grid_polygons(\n    dimensions: np.ndarray,\n    magnitude: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate distorted grid polygons based on input dimensions and magnitude.\n\n    This function creates a grid of polygons and applies random distortions to the internal vertices,\n    while keeping the boundary vertices fixed. The distortion is applied consistently across shared\n    vertices to avoid gaps or overlaps in the resulting grid.\n\n    Args:\n        dimensions (np.ndarray): A 3D array of shape (grid_height, grid_width, 4) where each element\n                                 is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.\n        magnitude (int): Maximum pixel-wise displacement for distortion. The actual displacement\n                         will be randomly chosen in the range [-magnitude, magnitude].\n        random_generator (np.random.Generator): A random number generator.\n\n    Returns:\n        np.ndarray: A 2D array of shape (total_cells, 8) where each row represents a distorted polygon\n                    as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.\n\n    Note:\n        - Only internal grid points are distorted; boundary points remain fixed.\n        - The function ensures consistent distortion across shared vertices of adjacent cells.\n        - The distortion is applied to the following points of each internal cell:\n            * Bottom-right of the cell above and to the left\n            * Bottom-left of the cell above\n            * Top-right of the cell to the left\n            * Top-left of the current cell\n        - Each square represents a cell, and the X marks indicate the coordinates where displacement occurs.\n            +--+--+--+--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--+--+--+--+\n        - For each X, the coordinates of the left, right, top, and bottom edges\n          in the four adjacent cells are displaced.\n\n    Example:\n        >>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n        ...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n        >>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n        >>> distorted.shape\n        (4, 8)\n    \"\"\"\n    grid_height, grid_width = dimensions.shape[:2]\n    total_cells = grid_height * grid_width\n\n    # Initialize polygons\n    polygons = np.zeros((total_cells, 8), dtype=np.float32)\n    polygons[:, 0:2] = dimensions.reshape(-1, 4)[:, [0, 1]]  # x1, y1\n    polygons[:, 2:4] = dimensions.reshape(-1, 4)[:, [2, 1]]  # x2, y1\n    polygons[:, 4:6] = dimensions.reshape(-1, 4)[:, [2, 3]]  # x2, y2\n    polygons[:, 6:8] = dimensions.reshape(-1, 4)[:, [0, 3]]  # x1, y2\n\n    # Generate displacements for internal grid points only\n    internal_points_height, internal_points_width = grid_height - 1, grid_width - 1\n    displacements = random_generator.integers(\n        -magnitude,\n        magnitude + 1,\n        size=(internal_points_height, internal_points_width, 2),\n    ).astype(np.float32)\n\n    # Apply displacements to internal polygon vertices\n    for i in range(1, grid_height):\n        for j in range(1, grid_width):\n            dx, dy = displacements[i - 1, j - 1]\n\n            # Bottom-right of cell (i-1, j-1)\n            polygons[(i - 1) * grid_width + (j - 1), 4:6] += [dx, dy]\n\n            # Bottom-left of cell (i-1, j)\n            polygons[(i - 1) * grid_width + j, 6:8] += [dx, dy]\n\n            # Top-right of cell (i, j-1)\n            polygons[i * grid_width + (j - 1), 2:4] += [dx, dy]\n\n            # Top-left of cell (i, j)\n            polygons[i * grid_width + j, 0:2] += [dx, dy]\n\n    return polygons\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_grid","title":"def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]","text":"

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)\n>>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n>>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n>>> num_steps = 5\n>>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n>>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_grid(\n    image_shape: tuple[int, int],\n    steps_x: list[float],\n    steps_y: list[float],\n    num_steps: int,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate a distorted grid for image transformation based on given step sizes.\n\n    This function creates two 2D arrays (map_x and map_y) that represent a distorted version\n    of the original image grid. These arrays can be used with OpenCV's remap function to\n    apply grid distortion to an image.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        steps_x (list[float]): List of step sizes for the x-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the x direction.\n        steps_y (list[float]): List of step sizes for the y-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the y direction.\n        num_steps (int): The number of steps to divide each axis into. This determines\n            the granularity of the distortion grid.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing two 2D numpy arrays:\n            - map_x: A 2D array of float32 values representing the x-coordinates\n              of the distorted grid.\n            - map_y: A 2D array of float32 values representing the y-coordinates\n              of the distorted grid.\n\n    Note:\n        - The function generates a grid where each cell can be distorted independently.\n        - The distortion is controlled by the steps_x and steps_y parameters, which\n          determine how much each grid line is shifted.\n        - The resulting map_x and map_y can be used directly with cv2.remap() to\n          apply the distortion to an image.\n        - The distortion is applied smoothly across each grid cell using linear\n          interpolation.\n\n    Example:\n        >>> image_shape = (100, 100)\n        >>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n        >>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n        >>> num_steps = 5\n        >>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n        >>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n    \"\"\"\n    height, width = image_shape[:2]\n    x_step = width // num_steps\n    xx = np.zeros(width, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_x):\n        x = idx * x_step\n        start = int(x)\n        end = min(int(x) + x_step, width)\n        cur = prev + x_step * step\n        xx[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    y_step = height // num_steps\n    yy = np.zeros(height, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_y):\n        y = idx * y_step\n        start = int(y)\n        end = min(int(y) + y_step, height)\n        cur = prev + y_step * step\n        yy[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    return np.meshgrid(xx, yy)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_reflected_bboxes","title":"def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_bboxes(\n    bboxes: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected bounding boxes for the entire reflection grid.\n\n    Args:\n        bboxes (np.ndarray): Original bounding boxes.\n        grid_dims (dict[str, tuple[int, int]]): Grid dimensions and original position.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted bounding boxes for the entire grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of bboxes\n    bboxes_hflipped = flip_bboxes(bboxes, flip_horizontal=True, image_shape=image_shape)\n    bboxes_vflipped = flip_bboxes(bboxes, flip_vertical=True, image_shape=image_shape)\n    bboxes_hvflipped = flip_bboxes(\n        bboxes,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [\n            original_col * cols,\n            original_row * rows,\n            original_col * cols,\n            original_row * rows,\n        ],\n    )\n    bboxes = shift_bboxes(bboxes, shift_vector)\n    bboxes_hflipped = shift_bboxes(bboxes_hflipped, shift_vector)\n    bboxes_vflipped = shift_bboxes(bboxes_vflipped, shift_vector)\n    bboxes_hvflipped = shift_bboxes(bboxes_hvflipped, shift_vector)\n\n    new_bboxes = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of bboxes to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes\n            elif (grid_row - original_row) % 2 == 0:\n                current_bboxes = bboxes_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes_vflipped\n            else:\n                current_bboxes = bboxes_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                ],\n            )\n            shifted_bboxes = shift_bboxes(current_bboxes, cell_shift)\n\n            new_bboxes.append(shifted_bboxes)\n\n    result = np.vstack(new_bboxes)\n\n    return shift_bboxes(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_reflected_keypoints","title":"def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols) - \"original_position\": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_keypoints(\n    keypoints: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected keypoints for the entire reflection grid.\n\n    This function creates a grid of keypoints by reflecting and shifting the original keypoints.\n    It handles both centered and non-centered grids based on the `center_in_origin` parameter.\n\n    Args:\n        keypoints (np.ndarray): Original keypoints array of shape (N, 4+), where N is the number of keypoints,\n                                and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).\n        grid_dims (dict[str, tuple[int, int]]): A dictionary containing grid dimensions and original position.\n            It should have the following keys:\n            - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols)\n            - \"original_position\": tuple[int, int] representing (original_row, original_col)\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool, optional): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted keypoints for the entire grid. The shape is\n                    (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.\n\n    Note:\n        - The function handles keypoint flipping and shifting to create a grid of reflected keypoints.\n        - It preserves the angle and scale information of the keypoints during transformations.\n        - The resulting grid can be either centered at the origin or positioned based on the original grid.\n    \"\"\"\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of keypoints\n    keypoints_hflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        image_shape=image_shape,\n    )\n    keypoints_vflipped = flip_keypoints(\n        keypoints,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n    keypoints_hvflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    rows, cols = image_shape[:2]\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [original_col * cols, original_row * rows, 0, 0],\n    )  # Only shift x and y\n    keypoints = shift_keypoints(keypoints, shift_vector)\n    keypoints_hflipped = shift_keypoints(keypoints_hflipped, shift_vector)\n    keypoints_vflipped = shift_keypoints(keypoints_vflipped, shift_vector)\n    keypoints_hvflipped = shift_keypoints(keypoints_hvflipped, shift_vector)\n\n    new_keypoints = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of keypoints to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints\n            elif (grid_row - original_row) % 2 == 0:\n                current_keypoints = keypoints_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints_vflipped\n            else:\n                current_keypoints = keypoints_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    0,\n                    0,\n                ],\n            )\n            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)\n\n            new_keypoints.append(shifted_keypoints)\n\n    result = np.vstack(new_keypoints)\n\n    return shift_keypoints(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_shuffled_splits","title":"def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]","text":"

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_shuffled_splits(\n    size: int,\n    divisions: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate shuffled splits for a given dimension size and number of divisions.\n\n    Args:\n        size (int): Total size of the dimension (height or width).\n        divisions (int): Number of divisions (rows or columns).\n        random_generator (np.random.Generator | None): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: Cumulative edges of the shuffled intervals.\n    \"\"\"\n    intervals = almost_equal_intervals(size, divisions)\n    random_generator.shuffle(intervals)\n    return np.insert(np.cumsum(intervals), 0, 0)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_camera_matrix_distortion_maps","title":"def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_camera_matrix_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using camera matrix model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n    camera_matrix = np.array(\n        [[width, 0, center_xy[0]], [0, height, center_xy[1]], [0, 0, 1]],\n        dtype=np.float32,\n    )\n    distortion = np.array([k, k, 0, 0, 0], dtype=np.float32)\n    return cv2.initUndistortRectifyMap(\n        camera_matrix,\n        distortion,\n        None,\n        None,\n        (width, height),\n        cv2.CV_32FC1,\n    )\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_dimension_padding","title":"def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]","text":"

Calculate padding for a single dimension.

Parameters:

Name Type Description current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_dimension_padding(\n    current_size: int,\n    min_size: int | None,\n    divisor: int | None,\n) -> tuple[int, int]:\n    \"\"\"Calculate padding for a single dimension.\n\n    Args:\n        current_size: Current size of the dimension\n        min_size: Minimum size requirement, if any\n        divisor: Divisor for padding to make size divisible, if any\n\n    Returns:\n        tuple[int, int]: (pad_before, pad_after)\n    \"\"\"\n    if min_size is not None:\n        if current_size < min_size:\n            pad_before = int((min_size - current_size) / 2.0)\n            pad_after = min_size - current_size - pad_before\n            return pad_before, pad_after\n    elif divisor is not None:\n        remainder = current_size % divisor\n        if remainder > 0:\n            total_pad = divisor - remainder\n            pad_before = total_pad // 2\n            pad_after = total_pad - pad_before\n            return pad_before, pad_after\n\n    return 0, 0\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_fisheye_distortion_maps","title":"def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using fisheye model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_fisheye_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using fisheye model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n\n    center_x, center_y = center_xy\n\n    # Create coordinate grid\n    y, x = np.mgrid[:height, :width].astype(np.float32)\n\n    x = x - center_x\n    y = y - center_y\n\n    # Calculate polar coordinates\n    r = np.sqrt(x * x + y * y)\n    theta = np.arctan2(y, x)\n\n    # Normalize radius by the maximum possible radius to keep distortion in check\n    max_radius = math.sqrt(max(center_x, width - center_x) ** 2 + max(center_y, height - center_y) ** 2)\n    r_norm = r / max_radius\n\n    # Apply fisheye distortion to normalized radius\n    r_dist = r * (1 + k * r_norm * r_norm)\n\n    # Convert back to cartesian coordinates\n    map_x = r_dist * np.cos(theta) + center_x\n    map_y = r_dist * np.sin(theta) + center_y\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_pad_grid_dimensions","title":"def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]","text":"

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def get_pad_grid_dimensions(\n    pad_top: int,\n    pad_bottom: int,\n    pad_left: int,\n    pad_right: int,\n    image_shape: tuple[int, int],\n) -> dict[str, tuple[int, int]]:\n    \"\"\"Calculate the dimensions of the grid needed for reflection padding and the position of the original image.\n\n    Args:\n        pad_top (int): Number of pixels to pad above the image.\n        pad_bottom (int): Number of pixels to pad below the image.\n        pad_left (int): Number of pixels to pad to the left of the image.\n        pad_right (int): Number of pixels to pad to the right of the image.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n\n    Returns:\n        dict[str, tuple[int, int]]: A dictionary containing:\n            - 'grid_shape': A tuple (grid_rows, grid_cols) where:\n                - grid_rows (int): Number of times the image needs to be repeated vertically.\n                - grid_cols (int): Number of times the image needs to be repeated horizontally.\n            - 'original_position': A tuple (original_row, original_col) where:\n                - original_row (int): Row index of the original image in the grid.\n                - original_col (int): Column index of the original image in the grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    grid_rows = 1 + math.ceil(pad_top / rows) + math.ceil(pad_bottom / rows)\n    grid_cols = 1 + math.ceil(pad_left / cols) + math.ceil(pad_right / cols)\n    original_row = math.ceil(pad_top / rows)\n    original_col = math.ceil(pad_left / cols)\n\n    return {\n        \"grid_shape\": (grid_rows, grid_cols),\n        \"original_position\": (original_row, original_col),\n    }\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_padding_params","title":"def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]","text":"

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_padding_params(\n    image_shape: tuple[int, int],\n    min_height: int | None,\n    min_width: int | None,\n    pad_height_divisor: int | None,\n    pad_width_divisor: int | None,\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate padding parameters based on target dimensions.\n\n    Args:\n        image_shape: (height, width) of the image\n        min_height: Minimum height requirement, if any\n        min_width: Minimum width requirement, if any\n        pad_height_divisor: Divisor for height padding, if any\n        pad_width_divisor: Divisor for width padding, if any\n\n    Returns:\n        tuple[int, int, int, int]: (pad_top, pad_bottom, pad_left, pad_right)\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    h_pad_top, h_pad_bottom = get_dimension_padding(\n        rows,\n        min_height,\n        pad_height_divisor,\n    )\n    w_pad_left, w_pad_right = get_dimension_padding(cols, min_width, pad_width_divisor)\n\n    return h_pad_top, h_pad_bottom, w_pad_left, w_pad_right\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.is_identity_matrix","title":"def is_identity_matrix (matrix) [view source on GitHub]","text":"

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_identity_matrix(matrix: np.ndarray) -> bool:\n    \"\"\"Check if the given matrix is an identity matrix.\n\n    Args:\n        matrix (np.ndarray): A 3x3 affine transformation matrix.\n\n    Returns:\n        bool: True if the matrix is an identity matrix, False otherwise.\n    \"\"\"\n    return np.allclose(matrix, np.eye(3, dtype=matrix.dtype))\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.is_valid_component","title":"def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]","text":"

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_valid_component(\n    component_area: float,\n    original_area: float,\n    min_area: float | None,\n    min_visibility: float | None,\n) -> bool:\n    \"\"\"Validate if a component meets the minimum requirements.\"\"\"\n    visibility = component_area / original_area\n    return (min_area is None or component_area >= min_area) and (min_visibility is None or visibility >= min_visibility)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_affine","title":"def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]","text":"

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])\n>>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n>>> scale = {'x': 1.5, 'y': 1.2}\n>>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_affine(\n    keypoints: np.ndarray,\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n    scale: XYFloat,\n    border_mode: int,\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to keypoints.\n\n    This function transforms keypoints using the given affine transformation matrix.\n    It handles reflection padding if necessary, updates coordinates, angles, and scales.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where N is the number of keypoints.\n                                Each keypoint is represented as [x, y, angle, scale, ...].\n        matrix (np.ndarray): The 2x3 or 3x3 affine transformation matrix.\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        scale (dict[str, float]): Dictionary containing scale factors for x and y directions.\n                                  Expected keys are 'x' and 'y'.\n        border_mode (int): Border mode for handling keypoints near image edges.\n                            Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.\n\n    Returns:\n        np.ndarray: Transformed keypoints array with the same shape as input.\n\n    Notes:\n        - The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.\n        - Coordinates (x, y) are transformed using the affine matrix.\n        - Angles are adjusted based on the rotation component of the affine transformation.\n        - Scales are multiplied by the maximum of x and y scale factors.\n        - The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.\n\n    Example:\n        >>> keypoints = np.array([[100, 100, 0, 1]])\n        >>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n        >>> scale = {'x': 1.5, 'y': 1.2}\n        >>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    if is_identity_matrix(matrix):\n        return keypoints\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        keypoints = generate_reflected_keypoints(\n            keypoints,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Extract x, y coordinates\n    xy = keypoints[:, :2]\n\n    # Ensure matrix is 2x3\n    if matrix.shape == (3, 3):\n        matrix = matrix[:2]\n\n    # Transform x, y coordinates\n    xy_transformed = cv2.transform(xy.reshape(-1, 1, 2), matrix).squeeze()\n\n    # Calculate angle adjustment\n    angle_adjustment = rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=False)\n\n    # Update angles\n    keypoints[:, 2] = keypoints[:, 2] + angle_adjustment\n\n    # Update scales\n    max_scale = max(scale[\"x\"], scale[\"y\"])\n\n    keypoints[:, 3] *= max_scale\n\n    # Update x, y coordinates\n    keypoints[:, :2] = xy_transformed\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_d4","title":"def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_d4(\n    keypoints: np.ndarray,\n    group_member: D4Type,\n    image_shape: tuple[int, int],\n    **params: Any,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a keypoint.\n\n    This function adjusts a keypoint's coordinates according to the specified `D_4` group transformation,\n    which includes rotations and reflections suitable for image processing tasks. These transformations account\n    for the dimensions of the image to ensure the keypoint remains within its boundaries.\n\n    Parameters:\n    - keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n    -group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.\n    - image_shape (tuple[int, int]): The shape of the image.\n    - params (Any): Not used\n\n    Returns:\n    - KeypointInternalType: The transformed keypoint.\n\n    Raises:\n    - ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.\n\n    Examples:\n    - Rotating a keypoint by 90 degrees in a 100x100 image:\n      `keypoint_d4((50, 30), 'r90', 100, 100)`\n      This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: keypoints_rot90(x, 1, image_shape),  # Rotate 90 degrees\n        \"r180\": lambda x: keypoints_rot90(x, 2, image_shape),  # Rotate 180 degrees\n        \"r270\": lambda x: keypoints_rot90(x, 3, image_shape),  # Rotate 270 degrees\n        \"v\": lambda x: keypoints_vflip(x, rows),  # Vertical flip\n        \"hvt\": lambda x: keypoints_transpose(\n            keypoints_rot90(x, 2, image_shape),\n        ),  # Reflect over anti diagonal\n        \"h\": lambda x: keypoints_hflip(x, cols),  # Horizontal flip\n        \"t\": lambda x: keypoints_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](keypoints)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_hflip","title":"def keypoints_hflip (keypoints, cols) [view source on GitHub]","text":"

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_hflip(keypoints: np.ndarray, cols: int) -> np.ndarray:\n    \"\"\"Flip keypoints horizontally around the y-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        cols: Image width.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip x-coordinates\n    flipped_keypoints[:, 0] = (cols - 1) - keypoints[:, 0]\n\n    # Adjust angles\n    flipped_keypoints[:, 2] = np.pi - keypoints[:, 2]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_rot90","title":"def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]","text":"

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description np.ndarray

The rotated keypoints with the same shape as the input.

Exceptions:

Type Description ValueError

If the factor is not in the set {0, 1, 2, 3}.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_rot90(\n    keypoints: np.ndarray,\n    factor: int,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        factor (int): The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n\n    Returns:\n        np.ndarray: The rotated keypoints with the same shape as the input.\n\n    Raises:\n        ValueError: If the factor is not in the set {0, 1, 2, 3}.\n    \"\"\"\n    if factor not in {0, 1, 2, 3}:\n        raise ValueError(\"Parameter factor must be in set {0, 1, 2, 3}\")\n\n    if factor == 0:\n        return keypoints\n\n    height, width = image_shape[:2]\n    rotated_keypoints = keypoints.copy().astype(np.float32)\n\n    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2]\n\n    if factor == 1:\n        rotated_keypoints[:, 0] = y\n        rotated_keypoints[:, 1] = width - 1 - x\n        rotated_keypoints[:, 2] = angle - np.pi / 2\n    elif factor == ROT90_180_FACTOR:\n        rotated_keypoints[:, 0] = width - 1 - x\n        rotated_keypoints[:, 1] = height - 1 - y\n        rotated_keypoints[:, 2] = angle - np.pi\n    elif factor == ROT90_270_FACTOR:\n        rotated_keypoints[:, 0] = height - 1 - y\n        rotated_keypoints[:, 1] = x\n        rotated_keypoints[:, 2] = angle + np.pi / 2\n\n    return rotated_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_scale","title":"def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]","text":"

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description np.ndarray

A numpy array of scaled keypoints with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_scale(\n    keypoints: np.ndarray,\n    scale_x: float,\n    scale_y: float,\n) -> np.ndarray:\n    \"\"\"Scales keypoints by scale_x and scale_y.\n\n    Args:\n        keypoints: A numpy array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        scale_x: Scale coefficient x-axis.\n        scale_y: Scale coefficient y-axis.\n\n    Returns:\n        A numpy array of scaled keypoints with the same shape as input.\n    \"\"\"\n    # Extract x, y, angle, and scale\n    x, y, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n    )\n\n    # Scale x and y\n    x_scaled = x * scale_x\n    y_scaled = y * scale_y\n\n    # Scale the keypoint scale by the maximum of scale_x and scale_y\n    scale_scaled = scale * max(scale_x, scale_y)\n\n    # Create the output array\n    scaled_keypoints = np.column_stack([x_scaled, y_scaled, angle, scale_scaled])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],\n        )\n\n    return scaled_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_transpose","title":"def keypoints_transpose (keypoints) [view source on GitHub]","text":"

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_transpose(keypoints: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes keypoints along the main diagonal.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n\n    Returns:\n        np.ndarray: An array of transposed keypoints with the same shape as the input.\n    \"\"\"\n    transposed_keypoints = keypoints.copy()\n\n    # Swap x and y coordinates\n    transposed_keypoints[:, [0, 1]] = keypoints[:, [1, 0]]\n\n    # Adjust angles to reflect the coordinate swap\n    angles = keypoints[:, 2]\n    transposed_keypoints[:, 2] = np.where(\n        angles <= np.pi,\n        np.pi / 2 - angles,\n        3 * np.pi / 2 - angles,\n    )\n\n    return transposed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_vflip","title":"def keypoints_vflip (keypoints, rows) [view source on GitHub]","text":"

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_vflip(keypoints: np.ndarray, rows: int) -> np.ndarray:\n    \"\"\"Flip keypoints vertically around the x-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        rows: Image height.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip y-coordinates\n    flipped_keypoints[:, 1] = (rows - 1) - keypoints[:, 1]\n\n    # Negate angles\n    flipped_keypoints[:, 2] = -keypoints[:, 2]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.perspective_bboxes","title":"def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n>>> image_shape = (100, 100)\n>>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n>>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef perspective_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Applies perspective transformation to bounding boxes.\n\n    This function transforms bounding boxes using the given perspective transformation matrix.\n    It handles bounding boxes with additional attributes beyond the standard coordinates.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (num_bboxes, 4+).\n                             Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n                             Additional columns beyond the first 4 are preserved unchanged.\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        matrix (np.ndarray): The perspective transformation matrix.\n        max_width (int): The maximum width of the output image.\n        max_height (int): The maximum height of the output image.\n        keep_size (bool): If True, maintains the original image size after transformation.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as input.\n                    The first 4 columns contain the transformed coordinates, and any\n                    additional columns are preserved from the input.\n\n    Note:\n        - This function modifies only the coordinate columns (first 4) of the input bounding boxes.\n        - Any additional attributes (columns beyond the first 4) are kept unchanged.\n        - The function handles denormalization and renormalization of coordinates internally.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n        >>> image_shape = (100, 100)\n        >>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n        >>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n    \"\"\"\n    height, width = image_shape[:2]\n    transformed_bboxes = bboxes.copy()\n    denormalized_coords = denormalize_bboxes(bboxes[:, :4], image_shape)\n\n    x_min, y_min, x_max, y_max = denormalized_coords.T\n    points = np.array(\n        [[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]],\n    ).transpose(2, 0, 1)\n    points_reshaped = points.reshape(-1, 1, 2)\n\n    transformed_points = cv2.perspectiveTransform(\n        points_reshaped.astype(np.float32),\n        matrix,\n    )\n    transformed_points = transformed_points.reshape(-1, 4, 2)\n\n    new_coords = np.array(\n        [[np.min(box[:, 0]), np.min(box[:, 1]), np.max(box[:, 0]), np.max(box[:, 1])] for box in transformed_points],\n    )\n\n    if keep_size:\n        scale_x, scale_y = width / max_width, height / max_height\n        new_coords[:, [0, 2]] *= scale_x\n        new_coords[:, [1, 3]] *= scale_y\n        output_shape = image_shape\n    else:\n        output_shape = (max_height, max_width)\n\n    normalized_coords = normalize_bboxes(new_coords, output_shape)\n    transformed_bboxes[:, :4] = normalized_coords\n\n    return transformed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.rotation2d_matrix_to_euler_angles","title":"def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]","text":"

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:\n    \"\"\"Args:\n    matrix (np.ndarray): Rotation matrix\n    y_up (bool): is Y axis looks up or down\n\n    \"\"\"\n    if y_up:\n        return np.arctan2(matrix[1, 0], matrix[0, 0])\n    return np.arctan2(-matrix[1, 0], matrix[0, 0])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.shift_bboxes","title":"def shift_bboxes (bboxes, shift_vector) [view source on GitHub]","text":"

Shift bounding boxes by a given vector.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Shift bounding boxes by a given vector.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where n is the number of bboxes\n                             and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].\n        shift_vector (np.ndarray): Vector to shift the bounding boxes by, with shape (4,) for\n                                   [shift_x, shift_y, shift_x, shift_y].\n\n    Returns:\n        np.ndarray: Shifted bounding boxes with the same shape as input.\n    \"\"\"\n    # Create a copy of the input array to avoid modifying it in-place\n    shifted_bboxes = bboxes.copy()\n\n    # Add the shift vector to the first 4 columns\n    shifted_bboxes[:, :4] += shift_vector\n\n    return shifted_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.shuffle_tiles_within_shape_groups","title":"def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]","text":"

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py Python
def shuffle_tiles_within_shape_groups(\n    shape_groups: dict[tuple[int, int], list[int]],\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Shuffles indices within each group of similar shapes and creates a list where each\n    index points to the index of the tile it should be mapped to.\n\n    Args:\n        shape_groups (dict[tuple[int, int], list[int]]): Groups of tile indices categorized by shape.\n        random_generator (np.random.Generator): The random generator to use for shuffling the indices.\n            If None, a new random generator will be used.\n\n    Returns:\n        list[int]: A list where each index is mapped to the new index of the tile after shuffling.\n    \"\"\"\n    # Initialize the output list with the same size as the total number of tiles, filled with -1\n    num_tiles = sum(len(indices) for indices in shape_groups.values())\n    mapping = [-1] * num_tiles\n\n    # Prepare the random number generator\n\n    for indices in shape_groups.values():\n        shuffled_indices = indices.copy()\n        random_generator.shuffle(shuffled_indices)\n\n        for old, new in zip(indices, shuffled_indices):\n            mapping[old] = new\n\n    return mapping\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.split_uniform_grid","title":"def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]","text":"

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py Python
def split_uniform_grid(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Splits an image shape into a uniform grid specified by the grid dimensions.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns).\n        random_generator (np.random.Generator): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).\n\n    Note:\n        The function uses `generate_shuffled_splits` to generate the splits for the height and width of the image.\n        The splits are then used to calculate the coordinates of the tiles.\n    \"\"\"\n    n_rows, n_cols = grid\n\n    height_splits = generate_shuffled_splits(\n        image_shape[0],\n        grid[0],\n        random_generator=random_generator,\n    )\n    width_splits = generate_shuffled_splits(\n        image_shape[1],\n        grid[1],\n        random_generator=random_generator,\n    )\n\n    # Calculate tiles coordinates\n    tiles = [\n        (height_splits[i], width_splits[j], height_splits[i + 1], width_splits[j + 1])\n        for i in range(n_rows)\n        for j in range(n_cols)\n    ]\n\n    return np.array(tiles, dtype=np.int16)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.swap_tiles_on_image","title":"def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]","text":"

Swap tiles on the image according to the new format.

Parameters:

Name Type Description image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_image(\n    image: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int] | None = None,\n) -> np.ndarray:\n    \"\"\"Swap tiles on the image according to the new format.\n\n    Args:\n        image: Input image.\n        tiles: Array of tiles with each tile as [start_y, start_x, end_y, end_x].\n        mapping: list of new tile indices.\n\n    Returns:\n        np.ndarray: Output image with tiles swapped according to the random shuffle.\n    \"\"\"\n    # If no tiles are provided, return a copy of the original image\n    if tiles.size == 0 or mapping is None:\n        return image.copy()\n\n    # Create a copy of the image to retain original for reference\n    new_image = np.empty_like(image)\n    for num, new_index in enumerate(mapping):\n        start_y, start_x, end_y, end_x = tiles[new_index]\n        start_y_orig, start_x_orig, end_y_orig, end_x_orig = tiles[num]\n        # Assign the corresponding tile from the original image to the new image\n        new_image[start_y:end_y, start_x:end_x] = image[\n            start_y_orig:end_y_orig,\n            start_x_orig:end_x_orig,\n        ]\n\n    return new_image\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.swap_tiles_on_keypoints","title":"def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]","text":"

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_keypoints(\n    keypoints: np.ndarray,\n    tiles: np.ndarray,\n    mapping: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Swap the positions of keypoints based on a tile mapping.\n\n    This function takes a set of keypoints and repositions them according to a mapping of tile swaps.\n    Keypoints are moved from their original tiles to new positions in the swapped tiles.\n\n    Args:\n        keypoints (np.ndarray): A 2D numpy array of shape (N, 2) where N is the number of keypoints.\n                                Each row represents a keypoint's (x, y) coordinates.\n        tiles (np.ndarray): A 2D numpy array of shape (M, 4) where M is the number of tiles.\n                            Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.\n        mapping (np.ndarray): A 1D numpy array of shape (M,) where M is the number of tiles.\n                              Each element i contains the index of the tile that tile i should be swapped with.\n\n    Returns:\n        np.ndarray: A 2D numpy array of the same shape as the input keypoints, containing the new positions\n                    of the keypoints after the tile swap.\n\n    Raises:\n        RuntimeWarning: If any keypoint is not found within any tile.\n\n    Notes:\n        - Keypoints that do not fall within any tile will remain unchanged.\n        - The function assumes that the tiles do not overlap and cover the entire image space.\n    \"\"\"\n    if not keypoints.size:\n        return keypoints\n\n    # Broadcast keypoints and tiles for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    start_y, start_x, end_y, end_x = tiles.T  # Each shape: (num_tiles,)\n\n    # Check if each keypoint is inside each tile\n    in_tile = (kp_y >= start_y) & (kp_y < end_y) & (kp_x >= start_x) & (kp_x < end_x)\n\n    # Find which tile each keypoint belongs to\n    tile_indices = np.argmax(in_tile, axis=1)\n\n    # Check if any keypoint is not in any tile\n    not_in_any_tile = ~np.any(in_tile, axis=1)\n    if np.any(not_in_any_tile):\n        warn(\n            \"Some keypoints are not in any tile. They will be returned unchanged. This is unexpected and should be \"\n            \"investigated.\",\n            RuntimeWarning,\n            stacklevel=2,\n        )\n\n    # Get the new tile indices\n    new_tile_indices = np.array(mapping)[tile_indices]\n\n    # Calculate the offsets\n    old_start_x = tiles[tile_indices, 1]\n    old_start_y = tiles[tile_indices, 0]\n    new_start_x = tiles[new_tile_indices, 1]\n    new_start_y = tiles[new_tile_indices, 0]\n\n    # Apply the transformation\n    new_keypoints = keypoints.copy()\n    new_keypoints[:, 0] = (keypoints[:, 0] - old_start_x) + new_start_x\n    new_keypoints[:, 1] = (keypoints[:, 1] - old_start_y) + new_start_y\n\n    # Keep original coordinates for keypoints not in any tile\n    new_keypoints[not_in_any_tile] = keypoints[not_in_any_tile]\n\n    return new_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.to_distance_maps","title":"def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]","text":"

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def to_distance_maps(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    inverted: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate a ``(H,W,N)`` array of distance maps for ``N`` keypoints.\n\n    The ``n``-th distance map contains at every location ``(y, x)`` the\n    euclidean distance to the ``n``-th keypoint.\n\n    This function can be used as a helper when augmenting keypoints with a\n    method that only supports the augmentation of images.\n\n    Args:\n        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.\n                   Each row represents a keypoint's (x, y) coordinates.\n        image_shape: tuple[int, int] shape of the image (height, width)\n        inverted (bool): If ``True``, inverted distance maps are returned where each\n            distance value d is replaced by ``d/(d+1)``, i.e. the distance\n            maps have values in the range ``(0.0, 1.0]`` with ``1.0`` denoting\n            exactly the position of the respective keypoint.\n\n    Returns:\n        np.ndarray: A ``float32`` array of shape (H, W, N) containing ``N`` distance maps for ``N``\n            keypoints. Each location ``(y, x, n)`` in the array denotes the\n            euclidean distance at ``(y, x)`` to the ``n``-th keypoint.\n            If `inverted` is ``True``, the distance ``d`` is replaced\n            by ``d/(d+1)``. The height and width of the array match the\n            height and width in ``image_shape``.\n    \"\"\"\n    height, width = image_shape[:2]\n    if len(keypoints) == 0:\n        return np.zeros((height, width, 0), dtype=np.float32)\n\n    # Create coordinate grids\n    yy, xx = np.mgrid[:height, :width]\n\n    # Convert keypoints to numpy array\n    keypoints_array = np.array(keypoints)\n\n    # Compute distances for all keypoints at once\n    distances = np.sqrt(\n        (xx[..., np.newaxis] - keypoints_array[:, 0]) ** 2 + (yy[..., np.newaxis] - keypoints_array[:, 1]) ** 2,\n    )\n\n    if inverted:\n        return (1 / (distances + 1)).astype(np.float32)\n    return distances.astype(np.float32)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.tps_transform","title":"def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]","text":"

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def tps_transform(\n    target_points: np.ndarray,\n    control_points: np.ndarray,\n    nonlinear_weights: np.ndarray,\n    affine_weights: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply Thin Plate Spline transformation to points.\n\n    Args:\n        target_points: Points to transform with shape (num_targets, 2)\n        control_points: Original control points with shape (num_controls, 2)\n        nonlinear_weights: TPS kernel weights with shape (num_controls, 2)\n        affine_weights: Affine transformation weights with shape (3, 2)\n\n    Returns:\n        Transformed points with shape (num_targets, 2)\n\n    Note:\n        The transformation combines:\n        1. Nonlinear warping based on distances to control points\n        2. Global affine transformation (scale, rotation, translation)\n    \"\"\"\n    # Compute all pairwise distances at once: (num_targets, num_controls)\n    distances = np.linalg.norm(target_points[:, None] - control_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Prepare affine terms [1, x, y] for each point\n    affine_terms = np.c_[np.ones(len(target_points)), target_points]\n\n    # Combine nonlinear and affine transformations\n    return kernel_matrix @ nonlinear_weights + affine_terms @ affine_weights\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.transpose","title":"def transpose (img) [view source on GitHub]","text":"

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description img np.ndarray

Input array.

Returns:

Type Description np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py Python
def transpose(img: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes the first two dimensions of an array of any dimensionality.\n    Retains the order of any additional dimensions.\n\n    Args:\n        img (np.ndarray): Input array.\n\n    Returns:\n        np.ndarray: Transposed array.\n    \"\"\"\n    # Generate the new axes order\n    new_axes = list(range(img.ndim))\n    new_axes[0], new_axes[1] = 1, 0  # Swap the first two dimensions\n\n    # Transpose the array using the new axes order\n    return img.transpose(new_axes)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_bboxes","title":"def validate_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n>>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n>>> print(valid_bboxes)\n[[10 20 30 40]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
def validate_bboxes(bboxes: np.ndarray, image_shape: Sequence[int]) -> np.ndarray:\n    \"\"\"Validate bounding boxes and remove invalid ones.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid bounding boxes, potentially with fewer boxes than the input.\n\n    Example:\n        >>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n        >>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n        >>> print(valid_bboxes)\n        [[10 20 30 40]]\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    valid_indices = (x_max > 0) & (y_max > 0) & (x_min < cols) & (y_min < rows)\n\n    return bboxes[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_if_not_found_coords","title":"def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]","text":"

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_if_not_found_coords(\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None,\n) -> tuple[bool, float, float]:\n    \"\"\"Validate and process `if_not_found_coords` parameter.\"\"\"\n    if if_not_found_coords is None:\n        return True, -1, -1\n    if isinstance(if_not_found_coords, (tuple, list)):\n        if len(if_not_found_coords) != PAIR:\n            msg = \"Expected tuple/list 'if_not_found_coords' to contain exactly two entries.\"\n            raise ValueError(msg)\n        return False, if_not_found_coords[0], if_not_found_coords[1]\n    if isinstance(if_not_found_coords, dict):\n        return False, if_not_found_coords[\"x\"], if_not_found_coords[\"y\"]\n\n    msg = \"Expected if_not_found_coords to be None, tuple, list, or dict.\"\n    raise ValueError(msg)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_keypoints","title":"def validate_keypoints (keypoints, image_shape) [view source on GitHub]","text":"

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Validate keypoints and remove those that fall outside the image boundaries.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, M) where N is the number of keypoints\n                                and M >= 2. The first two columns represent x and y coordinates.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid keypoints that fall within the image boundaries.\n\n    Note:\n        This function only checks the x and y coordinates (first two columns) of the keypoints.\n        Any additional columns (e.g., angle, scale) are preserved for valid keypoints.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x, y = keypoints[:, 0], keypoints[:, 1]\n\n    valid_indices = (x >= 0) & (x < cols) & (y >= 0) & (y < rows)\n\n    return keypoints[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/resize/","title":"Resizing transforms (augmentations.geometric.resize)","text":""},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.LongestMaxSize","title":"class LongestMaxSize [view source on GitHub]","text":"

Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, Sequence[int]

Maximum size of the longest side after the transformation. When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must fit within these bounds - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

interpolation method. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the longest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / max(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = min(H_target/H, W_target/W)\n       This ensures both dimensions fit within the specified bounds.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> # Using max_size\n>>> transform1 = A.LongestMaxSize(max_size=1024)\n>>> # Input image (1500, 800) -> Output (1024, 546)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>> # Input (800, 1500) -> Output (546, 1024)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>>\n>>> # Common use case with padding\n>>> transform4 = A.Compose([\n...     A.LongestMaxSize(max_size=1024),\n...     A.PadIfNeeded(min_height=1024, min_width=1024),\n... ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class LongestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints,\n        keeping the aspect ratio.\n\n    Args:\n        max_size (int, Sequence[int], optional): Maximum size of the longest side after the transformation.\n            When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must fit within these bounds\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): interpolation method. Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the longest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / max(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = min(H_target/H, W_target/W)\n               This ensures both dimensions fit within the specified bounds.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> # Using max_size\n        >>> transform1 = A.LongestMaxSize(max_size=1024)\n        >>> # Input image (1500, 800) -> Output (1024, 546)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>> # Input (800, 1500) -> Output (546, 1024)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>>\n        >>> # Common use case with padding\n        >>> transform4 = A.Compose([\n        ...     A.LongestMaxSize(max_size=1024),\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024),\n        ... ])\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / max(img_h, img_w)\n        elif self.max_size_hw is not None:\n            # We know max_size_hw is not None here due to model validator\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on longest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = min(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.MaxSizeTransform","title":"class MaxSizeTransform (max_size=1024, max_size_hw=None, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that resize based on maximum size constraints.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class MaxSizeTransform(DualTransform):\n    \"\"\"Base class for transforms that resize based on maximum size constraints.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_size: int | list[int] | None\n        max_size_hw: tuple[int | None, int | None] | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def validate_size_parameters(self) -> Self:\n            if self.max_size is None and self.max_size_hw is None:\n                raise ValueError(\"Either max_size or max_size_hw must be specified\")\n            if self.max_size is not None and self.max_size_hw is not None:\n                raise ValueError(\"Only one of max_size or max_size_hw should be specified\")\n            return self\n\n    def __init__(\n        self,\n        max_size: int | Sequence[int] | None = 1024,\n        max_size_hw: tuple[int | None, int | None] | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_size = max_size\n        self.max_size_hw = max_size_hw\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = img.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(img, (new_height, new_width), interpolation=self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = mask.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(mask, (new_height, new_width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(images, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volume, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volumes, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(masks3d, *args, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_size\", \"max_size_hw\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.RandomScale","title":"class RandomScale (scale_limit=(-0.1, 0.1), interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly resize the input. Output image size is different from the input image size.

Parameters:

Name Type Description scale_limit float or tuple[float, float]

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The output image size is different from the input image size.
  • Scale factor is sampled independently per image side (width and height).
  • Bounding box coordinates are scaled accordingly.
  • Keypoint coordinates are scaled accordingly.

Mathematical formulation: Let (W, H) be the original image dimensions and (W', H') be the output dimensions. The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]]. Then, W' = W * s and H' = H * s.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> scaled_image = result['image']\n# scaled_image will have dimensions in the range [90, 110] x [90, 110]\n# (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class RandomScale(DualTransform):\n    \"\"\"Randomly resize the input. Output image size is different from the input image size.\n\n    Args:\n        scale_limit (float or tuple[float, float]): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The output image size is different from the input image size.\n        - Scale factor is sampled independently per image side (width and height).\n        - Bounding box coordinates are scaled accordingly.\n        - Keypoint coordinates are scaled accordingly.\n\n    Mathematical formulation:\n        Let (W, H) be the original image dimensions and (W', H') be the output dimensions.\n        The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]].\n        Then, W' = W * s and H' = H * s.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> scaled_image = result['image']\n        # scaled_image will have dimensions in the range [90, 110] x [90, 110]\n        # (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_limit: ScaleFloatType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(cls, v: ScaleFloatType) -> tuple[float, float]:\n            return to_tuple(v, bias=1.0)\n\n    def __init__(\n        self,\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def get_params(self) -> dict[str, float]:\n        return {\"scale\": self.py_random.uniform(*self.scale_limit)}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(img, scale, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(mask, scale, self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"interpolation\": self.interpolation,\n            \"mask_interpolation\": self.mask_interpolation,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n        }\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.Resize","title":"class Resize (height, width, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Resize the input to the given height and width.

Parameters:

Name Type Description height int

desired height of the output.

width int

desired width of the output.

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class Resize(DualTransform):\n    \"\"\"Resize the input to the given height and width.\n\n    Args:\n        height (int): desired height of the output.\n        width (int): desired width of the output.\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: int = Field(ge=1)\n        width: int = Field(ge=1)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(img, (self.height, self.width), interpolation=self.interpolation)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(mask, (self.height, self.width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        height, width = params[\"shape\"][:2]\n        scale_x = self.width / width\n        scale_y = self.height / height\n        return fgeometric.keypoints_scale(keypoints, scale_x, scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.SmallestMaxSize","title":"class SmallestMaxSize [view source on GitHub]","text":"

Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, list of int

Maximum size of smallest side of the image after the transformation. When using a list, max size will be randomly selected from the values in the list. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must be at least these values - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the smallest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be larger than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / min(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = max(H_target/H, W_target/W)\n       This ensures both dimensions are at least as large as specified.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Using max_size\n>>> transform1 = A.SmallestMaxSize(max_size=120)\n>>> # Input image (100, 150) -> Output (120, 180)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n>>> # Input (80, 160) -> Output (100, 200)\n>>> # Input (160, 80) -> Output (400, 200)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n>>> # Input (80, 160) -> Output (100, 200)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class SmallestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints,\n    keeping the aspect ratio.\n\n    Args:\n        max_size (int, list of int, optional): Maximum size of smallest side of the image after the transformation.\n            When using a list, max size will be randomly selected from the values in the list. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must be at least these values\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the smallest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be larger than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / min(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = max(H_target/H, W_target/W)\n               This ensures both dimensions are at least as large as specified.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Using max_size\n        >>> transform1 = A.SmallestMaxSize(max_size=120)\n        >>> # Input image (100, 150) -> Output (120, 180)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n        >>> # Input (80, 160) -> Output (100, 200)\n        >>> # Input (160, 80) -> Output (400, 200)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n        >>> # Input (80, 160) -> Output (100, 200)\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / min(img_h, img_w)\n        elif self.max_size_hw is not None:\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on smallest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = max(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/rotate/","title":"Rotation transforms (augmentations.geometric.functional)","text":""},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.RandomRotate90","title":"class RandomRotate90 [view source on GitHub]","text":"

Randomly rotate the input by 90 degrees zero or more times.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RandomRotate90(DualTransform):\n    \"\"\"Randomly rotate the input by 90 degrees zero or more times.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, factor: int, **params: Any) -> np.ndarray:\n        return fgeometric.rot90(img, factor)\n\n    def get_params(self) -> dict[str, int]:\n        # Random int in the range [0, 3]\n        return {\"factor\": self.py_random.randint(0, 3)}\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        factor: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_rot90(bboxes, factor)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        factor: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_rot90(keypoints, factor, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.Rotate","title":"class Rotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', crop_border=False, mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input by an angle selected randomly from the uniform distribution.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method str

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

crop_border bool

Whether to crop border after rotation. If True, the output image size might differ from the input. Default: False

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation angle is randomly selected for each execution within the range specified by 'limit'.
  • When 'crop_border' is False, the output image will have the same size as the input, potentially introducing black triangles in the corners.
  • When 'crop_border' is True, the output image is cropped to remove black triangles, which may result in a smaller image.
  • Bounding boxes are rotated and may change size or shape.
  • Keypoints are rotated around the center of the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. Each point (x, y) in the image is transformed to (x', y') by: [x'] cos(\u03b8) -sin(\u03b8) [cx] [y'] = sin(\u03b8) cos(\u03b8) + [cy] where (cx, cy) is the center of the image. 5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Rotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class Rotate(DualTransform):\n    \"\"\"Rotate the input by an angle selected randomly from the uniform distribution.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        rotate_method (str): Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'.\n            Default: 'largest_box'\n        crop_border (bool): Whether to crop border after rotation. If True, the output image size might differ\n            from the input. Default: False\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation angle is randomly selected for each execution within the range specified by 'limit'.\n        - When 'crop_border' is False, the output image will have the same size as the input, potentially\n          introducing black triangles in the corners.\n        - When 'crop_border' is True, the output image is cropped to remove black triangles, which may result\n          in a smaller image.\n        - Bounding boxes are rotated and may change size or shape.\n        - Keypoints are rotated around the center of the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [cos(\u03b8)  -sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [sin(\u03b8)   cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n        5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Rotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        crop_border: bool\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_value(self) -> Self:\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        crop_border: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.crop_border = crop_border\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            img,\n            matrix,\n            self.interpolation,\n            self.fill,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            mask,\n            matrix,\n            self.mask_interpolation,\n            self.fill_mask,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_out = fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            image_shape,\n            self.border_mode,\n            image_shape,\n        )\n        if self.crop_border:\n            return fcrops.crop_bboxes_by_coords(\n                bboxes_out,\n                (x_min, y_min, x_max, y_max),\n                image_shape,\n            )\n        return bboxes_out\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints_out = fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"][:2],\n            scale={\"x\": 1, \"y\": 1},\n            border_mode=self.border_mode,\n        )\n        if self.crop_border:\n            return fcrops.crop_keypoints_by_coords(\n                keypoints_out,\n                (x_min, y_min, x_max, y_max),\n            )\n        return keypoints_out\n\n    @staticmethod\n    def _rotated_rect_with_max_area(\n        height: int,\n        width: int,\n        angle: float,\n    ) -> dict[str, int]:\n        \"\"\"Given a rectangle of size wxh that has been rotated by 'angle' (in\n        degrees), computes the width and height of the largest possible\n        axis-aligned rectangle (maximal area) within the rotated rectangle.\n\n        Reference:\n            https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders\n        \"\"\"\n        angle = math.radians(angle)\n        width_is_longer = width >= height\n        side_long, side_short = (width, height) if width_is_longer else (height, width)\n\n        # since the solutions for angle, -angle and 180-angle are all the same,\n        # it is sufficient to look at the first quadrant and the absolute values of sin,cos:\n        sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))\n        if side_short <= 2.0 * sin_a * cos_a * side_long or abs(sin_a - cos_a) < SMALL_NUMBER:\n            # half constrained case: two crop corners touch the longer side,\n            # the other two corners are on the mid-line parallel to the longer line\n            x = 0.5 * side_short\n            wr, hr = (x / sin_a, x / cos_a) if width_is_longer else (x / cos_a, x / sin_a)\n        else:\n            # fully constrained case: crop touches all 4 sides\n            cos_2a = cos_a * cos_a - sin_a * sin_a\n            wr, hr = (\n                (width * cos_a - height * sin_a) / cos_2a,\n                (height * cos_a - width * sin_a) / cos_2a,\n            )\n\n        return {\n            \"x_min\": max(0, int(width / 2 - wr / 2)),\n            \"x_max\": min(width, int(width / 2 + wr / 2)),\n            \"y_min\": max(0, int(height / 2 - hr / 2)),\n            \"y_max\": min(height, int(height / 2 + hr / 2)),\n        }\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        angle = self.py_random.uniform(*self.limit)\n\n        if self.crop_border:\n            height, width = params[\"shape\"][:2]\n            out_params = self._rotated_rect_with_max_area(height, width, angle)\n        else:\n            out_params = {\"x_min\": -1, \"x_max\": -1, \"y_min\": -1, \"y_max\": -1}\n\n        center = fgeometric.center(params[\"shape\"][:2])\n        bbox_center = fgeometric.center_bbox(params[\"shape\"][:2])\n\n        translate: fgeometric.XYInt = {\"x\": 0, \"y\": 0}\n        shear: fgeometric.XYFloat = {\"x\": 0, \"y\": 0}\n        scale: fgeometric.XYFloat = {\"x\": 1, \"y\": 1}\n        rotate = angle\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            center,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_center,\n        )\n        out_params[\"matrix\"] = matrix\n        out_params[\"bbox_matrix\"] = bbox_matrix\n\n        return out_params\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"crop_border\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.RotateInitSchema","title":"class RotateInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RotateInitSchema(BaseTransformInitSchema):\n    limit: SymmetricRangeType\n\n    interpolation: InterpolationType\n    mask_interpolation: InterpolationType\n\n    border_mode: BorderModeType\n\n    fill: ColorType | None\n    fill_mask: ColorType | None\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.SafeRotate","title":"class SafeRotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.

This transformation ensures that the entire rotated image fits within the original frame by scaling it down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the rotation and scaling process.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method Literal[\"largest_box\", \"ellipse\"]

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation is performed around the center of the image.
  • After rotation, the image is scaled to fit within the original frame, which may cause some distortion.
  • The output image will always have the same dimensions as the input image.
  • Bounding boxes and keypoints are transformed along with the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. The scaling factor s is calculated to ensure the rotated image fits within the original frame: s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|), height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|)) 5. The combined transformation matrix T is: T = [scos(\u03b8) -ssin(\u03b8) tx] [ssin(\u03b8) scos(\u03b8) ty] where tx and ty are translation factors to keep the image centered. 6. Each point (x, y) in the image is transformed to (x', y') by: [x'] scos(\u03b8) ssin(\u03b8) [cx] [y'] = -ssin(\u03b8) scos(\u03b8) + [cy] where (cx, cy) is the center of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.SafeRotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n# scaled to fit within the original 100x100 frame\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class SafeRotate(Affine):\n    \"\"\"Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.\n\n    This transformation ensures that the entire rotated image fits within the original frame by scaling it\n    down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the\n    rotation and scaling process.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied\n            for masks.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): Method to rotate bounding boxes.\n            Should be 'largest_box' or 'ellipse'. Default: 'largest_box'\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation is performed around the center of the image.\n        - After rotation, the image is scaled to fit within the original frame, which may cause some distortion.\n        - The output image will always have the same dimensions as the input image.\n        - Bounding boxes and keypoints are transformed along with the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. The scaling factor s is calculated to ensure the rotated image fits within the original frame:\n           s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|),\n                   height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|))\n        5. The combined transformation matrix T is:\n           T = [s*cos(\u03b8)  -s*sin(\u03b8)  tx]\n               [s*sin(\u03b8)   s*cos(\u03b8)  ty]\n           where tx and ty are translation factors to keep the image centered.\n        6. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [s*cos(\u03b8)   s*sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [-s*sin(\u03b8)  s*cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.SafeRotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n        # scaled to fit within the original 100x100 frame\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            rotate=limit,\n            interpolation=interpolation,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            rotate_method=rotate_method,\n            fit_output=True,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.mask_interpolation = mask_interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"mask_interpolation\",\n        )\n\n    def _create_safe_rotate_matrix(\n        self,\n        angle: float,\n        center: tuple[float, float],\n        image_shape: tuple[int, int],\n    ) -> tuple[np.ndarray, dict[str, float]]:\n        height, width = image_shape[:2]\n        rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.0)\n\n        # Calculate new image size\n        abs_cos = abs(rotation_mat[0, 0])\n        abs_sin = abs(rotation_mat[0, 1])\n        new_w = int(height * abs_sin + width * abs_cos)\n        new_h = int(height * abs_cos + width * abs_sin)\n\n        # Adjust the rotation matrix to take into account the new size\n        rotation_mat[0, 2] += new_w / 2 - center[0]\n        rotation_mat[1, 2] += new_h / 2 - center[1]\n\n        # Calculate scaling factors\n        scale_x = width / new_w\n        scale_y = height / new_h\n\n        # Create scaling matrix\n        scale_mat = np.array([[scale_x, 0, 0], [0, scale_y, 0], [0, 0, 1]])\n\n        # Combine rotation and scaling\n        matrix = scale_mat @ np.vstack([rotation_mat, [0, 0, 1]])\n\n        return matrix, {\"x\": scale_x, \"y\": scale_y}\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        angle = self.py_random.uniform(*self.limit)\n\n        # Calculate centers for image and bbox\n        image_center = fgeometric.center(image_shape)\n        bbox_center = fgeometric.center_bbox(image_shape)\n\n        # Create matrices for image and bbox\n        matrix, scale = self._create_safe_rotate_matrix(\n            angle,\n            image_center,\n            image_shape,\n        )\n        bbox_matrix, _ = self._create_safe_rotate_matrix(\n            angle,\n            bbox_center,\n            image_shape,\n        )\n\n        return {\n            \"rotate\": angle,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": image_shape,\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/","title":"Geometric transforms (augmentations.geometric.transforms)","text":""},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Affine","title":"class Affine (scale=1, translate_percent=None, translate_px=None, rotate=0, shear=0, interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, fit_output=False, keep_ratio=False, rotate_method='largest_box', balanced_scale=False, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Augmentation to apply affine transformations to images.

Affine transformations involve:

- Translation (\"move\" image on the x-/y-axis)\n- Rotation\n- Scaling (\"zoom\" in/out)\n- Shear (move one side of the image, turning a square into a trapezoid)\n

All such transformations can create \"new\" pixels in the image without a defined content, e.g. if the image is translated to the left, pixels are created on the right. A method has to be defined to deal with these pixel values. The parameters fill and fill_mask of this class deal with this.

Some transformations involve interpolations between several pixels of the input image to generate output pixel values. The parameters interpolation and mask_interpolation deals with the method of interpolation used for this.

Parameters:

Name Type Description scale number, tuple of number or dict

Scaling factor to use, where 1.0 denotes \"no change\" and 0.5 is zoomed out to 50 percent of the original size. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That the same range will be used for both x- and y-axis. To keep the aspect ratio, set keep_ratio=True, then the same value will be used for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes. Note that when the keep_ratio=True, the x- and y-axis ranges should be the same.

translate_percent None, number, tuple of number or dict

Translation as a fraction of the image height/width (x-translation, y-translation), where 0 denotes \"no change\" and 0.5 denotes \"half of the axis size\". * If None then equivalent to 0.0 unless translate_px has a value other than None. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That sampled fraction value will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

translate_px None, int, tuple of int or dict

Translation in pixels. * If None then equivalent to 0 unless translate_percent has a value other than None. * If a single int, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the discrete interval [a..b]. That number will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

rotate number or tuple of number

Rotation in degrees (NOT radians), i.e. expected value range is around [-360, 360]. Rotation happens around the center of the image, not the top left corner as in some other frameworks. * If a number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b] and used as the rotation value.

shear number, tuple of number or dict

Shear in degrees (NOT radians), i.e. expected value range is around [-360, 360], with reasonable values being in the range of [-45, 45]. * If a number, then that value will be used for all images as the shear on the x-axis (no shear on the y-axis will be done). * If a tuple (a, b), then two value will be uniformly sampled per image from the interval [a, b] and be used as the x- and y-shear value. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

interpolation int

OpenCV interpolation flag.

mask_interpolation int

OpenCV interpolation flag.

fill ColorType

The constant value to use when filling in newly created pixels. (E.g. translating by 1px to the right will create a new 1px-wide column of pixels on the left of the image). The value is only used when mode=constant. The expected value range is [0, 255] for uint8 images.

fill_mask ColorType

Same as fill but only for masks.

border_mode int

OpenCV border flag.

fit_output bool

If True, the image plane size and position will be adjusted to tightly capture the whole image after affine transformation (translate_percent and translate_px are ignored). Otherwise (False), parts of the transformed image may end up outside the image plane. Fitting the output shape can be useful to avoid corners of the image being outside the image plane after applying rotations. Default: False

keep_ratio bool

When True, the original aspect ratio will be kept when the random scale is applied. Default: False.

rotate_method Literal[\"largest_box\", \"ellipse\"]

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"

balanced_scale bool

When True, scaling factors are chosen to be either entirely below or above 1, ensuring balanced scaling. Default: False.

This is important because without it, scaling tends to lean towards upscaling. For example, if we want the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly from [0.5, 2]. With balanced_scale, the function ensures that half the time, the scaling factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in). This makes the zooming in and out process more balanced.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

[1] https://arxiv.org/abs/2109.13488

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Affine(DualTransform):\n    \"\"\"Augmentation to apply affine transformations to images.\n\n    Affine transformations involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a defined content, e.g.\n    if the image is translated to the left, pixels are created on the right.\n    A method has to be defined to deal with these pixel values.\n    The parameters `fill` and `fill_mask` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameters `interpolation` and\n    `mask_interpolation` deals with the method of interpolation used for this.\n\n    Args:\n        scale (number, tuple of number or dict): Scaling factor to use, where ``1.0`` denotes \"no change\" and\n            ``0.5`` is zoomed out to ``50`` percent of the original size.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That the same range will be used for both x- and y-axis. To keep the aspect ratio, set\n                  ``keep_ratio=True``, then the same value will be used for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes. Note that when\n                  the ``keep_ratio=True``, the x- and y-axis ranges should be the same.\n        translate_percent (None, number, tuple of number or dict): Translation as a fraction of the image height/width\n            (x-translation, y-translation), where ``0`` denotes \"no change\"\n            and ``0.5`` denotes \"half of the axis size\".\n                * If ``None`` then equivalent to ``0.0`` unless `translate_px` has a value other than ``None``.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That sampled fraction value will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        translate_px (None, int, tuple of int or dict): Translation in pixels.\n                * If ``None`` then equivalent to ``0`` unless `translate_percent` has a value other than ``None``.\n                * If a single int, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from\n                  the discrete interval ``[a..b]``. That number will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        rotate (number or tuple of number): Rotation in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``. Rotation happens around the *center* of the image,\n            not the top left corner as in some other frameworks.\n                * If a number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``\n                  and used as the rotation value.\n        shear (number, tuple of number or dict): Shear in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``, with reasonable values being in the range of ``[-45, 45]``.\n                * If a number, then that value will be used for all images as\n                  the shear on the x-axis (no shear on the y-axis will be done).\n                * If a tuple ``(a, b)``, then two value will be uniformly sampled per image\n                  from the interval ``[a, b]`` and be used as the x- and y-shear value.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        interpolation (int): OpenCV interpolation flag.\n        mask_interpolation (int): OpenCV interpolation flag.\n        fill (ColorType): The constant value to use when filling in newly created pixels.\n            (E.g. translating by 1px to the right will create a new 1px-wide column of pixels\n            on the left of the image).\n            The value is only used when `mode=constant`. The expected value range is ``[0, 255]`` for ``uint8`` images.\n        fill_mask (ColorType): Same as fill but only for masks.\n        border_mode (int): OpenCV border flag.\n        fit_output (bool): If True, the image plane size and position will be adjusted to tightly capture\n            the whole image after affine transformation (`translate_percent` and `translate_px` are ignored).\n            Otherwise (``False``),  parts of the transformed image may end up outside the image plane.\n            Fitting the output shape can be useful to avoid corners of the image being outside the image plane\n            after applying rotations. Default: False\n        keep_ratio (bool): When True, the original aspect ratio will be kept when the random scale is applied.\n            Default: False.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): rotation method used for the bounding boxes.\n            Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"\n        balanced_scale (bool): When True, scaling factors are chosen to be either entirely below or above 1,\n            ensuring balanced scaling. Default: False.\n\n            This is important because without it, scaling tends to lean towards upscaling. For example, if we want\n            the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is\n            three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly\n            from [0.5, 2]. With `balanced_scale`, the  function ensures that half the time, the scaling\n            factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in).\n            This makes the zooming in and out process more balanced.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        [1] https://arxiv.org/abs/2109.13488\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: ScaleFloatType | fgeometric.XYFloatScale\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None\n        rotate: ScaleFloatType\n        shear: ScaleFloatType | fgeometric.XYFloatScale\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        cval: ColorType | None\n        cval_mask: ColorType | None\n        mode: BorderModeType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        fit_output: bool\n        keep_ratio: bool\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        balanced_scale: bool\n\n        @field_validator(\"shear\", \"scale\")\n        @classmethod\n        def process_shear(\n            cls,\n            value: ScaleFloatType | fgeometric.XYFloatScale,\n            info: ValidationInfo,\n        ) -> fgeometric.XYFloatDict:\n            return cast(\n                fgeometric.XYFloatDict,\n                cls._handle_dict_arg(value, info.field_name),\n            )\n\n        @field_validator(\"rotate\")\n        @classmethod\n        def process_rotate(\n            cls,\n            value: ScaleFloatType,\n        ) -> tuple[float, float]:\n            return to_tuple(value, value)\n\n        @model_validator(mode=\"after\")\n        def handle_translate(self) -> Self:\n            if self.translate_percent is None and self.translate_px is None:\n                self.translate_px = 0\n\n            if self.translate_percent is not None and self.translate_px is not None:\n                msg = \"Expected either translate_percent or translate_px to be provided, but both were provided.\"\n                raise ValueError(msg)\n\n            if self.translate_percent is not None:\n                self.translate_percent = self._handle_dict_arg(\n                    self.translate_percent,\n                    \"translate_percent\",\n                    default=0.0,\n                )  # type: ignore[assignment]\n\n            if self.translate_px is not None:\n                self.translate_px = self._handle_dict_arg(\n                    self.translate_px,\n                    \"translate_px\",\n                    default=0,\n                )  # type: ignore[assignment]\n\n            return self\n\n        @staticmethod\n        def _handle_dict_arg(\n            val: ScaleType | fgeometric.XYFloatScale | fgeometric.XYIntScale,\n            name: str | None,\n            default: float = 1.0,\n        ) -> dict[str, Any]:\n            if isinstance(val, dict):\n                if \"x\" not in val and \"y\" not in val:\n                    raise ValueError(\n                        f'Expected {name} dictionary to contain at least key \"x\" or key \"y\". Found neither of them.',\n                    )\n                x = val.get(\"x\", default)\n                y = val.get(\"y\", default)\n                return {\"x\": to_tuple(x, x), \"y\": to_tuple(y, y)}  # type: ignore[arg-type]\n            return {\"x\": to_tuple(val, val), \"y\": to_tuple(val, val)}\n\n        @model_validator(mode=\"after\")\n        def validate_fill_types(self) -> Self:\n            if self.cval is not None:\n                self.fill = self.cval\n                warn(\"cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n            if self.cval_mask is not None:\n                self.fill_mask = self.cval_mask\n                warn(\"cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n            if self.mode is not None:\n                self.border_mode = self.mode\n                warn(\"mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType | fgeometric.XYFloatScale = 1,\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None = None,\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None = None,\n        rotate: ScaleFloatType = 0,\n        shear: ScaleFloatType | fgeometric.XYFloatScale = 0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: ColorType | None = None,\n        cval_mask: ColorType | None = None,\n        mode: int | None = None,\n        fit_output: bool = False,\n        keep_ratio: bool = False,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        balanced_scale: bool = False,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n        self.scale = cast(fgeometric.XYFloatDict, scale)\n        self.translate_percent = cast(fgeometric.XYFloatDict, translate_percent)\n        self.translate_px = cast(fgeometric.XYIntDict, translate_px)\n        self.rotate = cast(tuple[float, float], rotate)\n        self.fit_output = fit_output\n        self.shear = cast(fgeometric.XYFloatDict, shear)\n        self.keep_ratio = keep_ratio\n        self.rotate_method = rotate_method\n        self.balanced_scale = balanced_scale\n\n        if self.keep_ratio and self.scale[\"x\"] != self.scale[\"y\"]:\n            raise ValueError(\n                f\"When keep_ratio is True, the x and y scale range should be identical. got {self.scale}\",\n            )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"fill\",\n            \"border_mode\",\n            \"scale\",\n            \"translate_percent\",\n            \"translate_px\",\n            \"rotate\",\n            \"fit_output\",\n            \"shear\",\n            \"fill_mask\",\n            \"keep_ratio\",\n            \"rotate_method\",\n            \"balanced_scale\",\n        )\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            img,\n            matrix,\n            interpolation=self.interpolation,\n            fill=self.fill,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            mask,\n            matrix,\n            interpolation=self.mask_interpolation,\n            fill=self.fill_mask,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            params[\"shape\"][:2],\n            self.border_mode,\n            output_shape,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        scale: fgeometric.XYFloat,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"],\n            scale,\n            self.border_mode,\n        )\n\n    @staticmethod\n    def get_scale(\n        scale: fgeometric.XYFloatDict,\n        keep_ratio: bool,\n        balanced_scale: bool,\n        random_state: random.Random,\n    ) -> fgeometric.XYFloat:\n        result_scale = {}\n        for key, value in scale.items():\n            if isinstance(value, (int, float)):\n                result_scale[key] = float(value)\n            elif isinstance(value, tuple):\n                if balanced_scale:\n                    lower_interval = (value[0], 1.0) if value[0] < 1 else None\n                    upper_interval = (1.0, value[1]) if value[1] > 1 else None\n\n                    if lower_interval is not None and upper_interval is not None:\n                        selected_interval = random_state.choice(\n                            [lower_interval, upper_interval],\n                        )\n                    elif lower_interval is not None:\n                        selected_interval = lower_interval\n                    elif upper_interval is not None:\n                        selected_interval = upper_interval\n                    else:\n                        result_scale[key] = 1.0\n                        continue\n\n                    result_scale[key] = random_state.uniform(*selected_interval)\n                else:\n                    result_scale[key] = random_state.uniform(*value)\n            else:\n                raise TypeError(\n                    f\"Invalid scale value for key {key}: {value}. Expected a float or a tuple of two floats.\",\n                )\n\n        if keep_ratio:\n            result_scale[\"y\"] = result_scale[\"x\"]\n\n        return cast(fgeometric.XYFloat, result_scale)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        translate = self._get_translate_params(image_shape)\n        shear = self._get_shear_params()\n        scale = self.get_scale(\n            self.scale,\n            self.keep_ratio,\n            self.balanced_scale,\n            self.py_random,\n        )\n        rotate = self.py_random.uniform(*self.rotate)\n\n        image_shift = fgeometric.center(image_shape)\n        bbox_shift = fgeometric.center_bbox(image_shape)\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            image_shift,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_shift,\n        )\n\n        if self.fit_output:\n            matrix, output_shape = fgeometric.compute_affine_warp_output_shape(\n                matrix,\n                image_shape,\n            )\n            bbox_matrix, _ = fgeometric.compute_affine_warp_output_shape(\n                bbox_matrix,\n                image_shape,\n            )\n        else:\n            output_shape = image_shape\n\n        return {\n            \"rotate\": rotate,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": output_shape,\n        }\n\n    def _get_translate_params(self, image_shape: tuple[int, int]) -> fgeometric.XYInt:\n        height, width = image_shape[:2]\n        if self.translate_px is not None:\n            return {\n                \"x\": self.py_random.randint(*self.translate_px[\"x\"]),\n                \"y\": self.py_random.randint(*self.translate_px[\"y\"]),\n            }\n        if self.translate_percent is not None:\n            translate = {key: self.py_random.uniform(*value) for key, value in self.translate_percent.items()}\n            return cast(\n                fgeometric.XYInt,\n                {\"x\": int(translate[\"x\"] * width), \"y\": int(translate[\"y\"] * height)},\n            )\n        return cast(fgeometric.XYInt, {\"x\": 0, \"y\": 0})\n\n    def _get_shear_params(self) -> fgeometric.XYFloat:\n        return {\n            \"x\": -self.py_random.uniform(*self.shear[\"x\"]),\n            \"y\": -self.py_random.uniform(*self.shear[\"y\"]),\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.BaseDistortion","title":"class BaseDistortion (interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Base class for distortion-based transformations.

This class provides a foundation for implementing various types of image distortions, such as optical distortions, grid distortions, and elastic transformations. It handles the common operations of applying distortions to images, masks, bounding boxes, and keypoints.

Parameters:

Name Type Description interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR, cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This is an abstract base class and should not be used directly.
  • Subclasses should implement the get_params_dependent_on_data method to generate the distortion maps (map_x and map_y).
  • The distortion is applied consistently across all targets (image, mask, bboxes, keypoints) to maintain coherence in the augmented data.

Example of a subclass: class CustomDistortion(BaseDistortion): def init(self, args, **kwargs): super().init(args, **kwargs) # Add custom parameters here

    def get_params_dependent_on_data(self, params, data):\n        # Generate and return map_x and map_y based on the distortion logic\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self):\n        return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class BaseDistortion(DualTransform):\n    \"\"\"Base class for distortion-based transformations.\n\n    This class provides a foundation for implementing various types of image distortions,\n    such as optical distortions, grid distortions, and elastic transformations. It handles\n    the common operations of applying distortions to images, masks, bounding boxes, and keypoints.\n\n    Args:\n        interpolation (int): Interpolation method to be used for image transformation.\n            Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR,\n            cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This is an abstract base class and should not be used directly.\n        - Subclasses should implement the `get_params_dependent_on_data` method to generate\n          the distortion maps (map_x and map_y).\n        - The distortion is applied consistently across all targets (image, mask, bboxes, keypoints)\n          to maintain coherence in the augmented data.\n\n    Example of a subclass:\n        class CustomDistortion(BaseDistortion):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                # Add custom parameters here\n\n            def get_params_dependent_on_data(self, params, data):\n                # Generate and return map_x and map_y based on the distortion logic\n                return {\"map_x\": map_x, \"map_y\": map_y}\n\n            def get_transform_init_args_names(self):\n                return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            img,\n            map_x,\n            map_y,\n            self.interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            mask,\n            map_x,\n            map_y,\n            self.mask_interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        bboxes_returned = fgeometric.remap_bboxes(\n            bboxes_denorm,\n            map_x,\n            map_y,\n            image_shape,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap_keypoints(keypoints, map_x, map_y, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.D4","title":"class D4 (p=1, always_apply=None) [view source on GitHub]","text":"

Applies one of the eight possible D4 dihedral group transformations to a square-shaped input, maintaining the square shape. These transformations correspond to the symmetries of a square, including rotations and reflections.

The D4 group transformations include: - 'e' (identity): No transformation is applied. - 'r90' (rotation by 90 degrees counterclockwise) - 'r180' (rotation by 180 degrees) - 'r270' (rotation by 270 degrees counterclockwise) - 'v' (reflection across the vertical midline) - 'hvt' (reflection across the anti-diagonal) - 'h' (reflection across the horizontal midline) - 't' (reflection across the main diagonal)

Even if the probability (p) of applying the transform is set to 1, the identity transformation 'e' may still occur, which means the input will remain unchanged in one out of eight cases.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for augmenting data that does not have a clear orientation, such as top-view satellite or drone imagery, or certain types of medical images.
  • The input image should be square-shaped for optimal results. Non-square inputs may lead to unexpected behavior or distortions.
  • When applied to bounding boxes or keypoints, their coordinates will be adjusted according to the selected transformation.
  • This transform preserves the aspect ratio and size of the input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.D4(p=1.0),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed['image']\n# The resulting image will be one of the 8 possible D4 transformations of the input\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class D4(DualTransform):\n    \"\"\"Applies one of the eight possible D4 dihedral group transformations to a square-shaped input,\n    maintaining the square shape. These transformations correspond to the symmetries of a square,\n    including rotations and reflections.\n\n    The D4 group transformations include:\n    - 'e' (identity): No transformation is applied.\n    - 'r90' (rotation by 90 degrees counterclockwise)\n    - 'r180' (rotation by 180 degrees)\n    - 'r270' (rotation by 270 degrees counterclockwise)\n    - 'v' (reflection across the vertical midline)\n    - 'hvt' (reflection across the anti-diagonal)\n    - 'h' (reflection across the horizontal midline)\n    - 't' (reflection across the main diagonal)\n\n    Even if the probability (`p`) of applying the transform is set to 1, the identity transformation\n    'e' may still occur, which means the input will remain unchanged in one out of eight cases.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for augmenting data that does not have a clear orientation,\n          such as top-view satellite or drone imagery, or certain types of medical images.\n        - The input image should be square-shaped for optimal results. Non-square inputs may lead to\n          unexpected behavior or distortions.\n        - When applied to bounding boxes or keypoints, their coordinates will be adjusted according\n          to the selected transformation.\n        - This transform preserves the aspect ratio and size of the input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.D4(p=1.0),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed['image']\n        # The resulting image will be one of the 8 possible D4 transformations of the input\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.d4(img, group_element)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_d4(bboxes, group_element)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_d4(keypoints, group_element, params[\"shape\"])\n\n    def get_params(self) -> dict[str, D4Type]:\n        return {\n            \"group_element\": self.random_generator.choice(d4_group_elements),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ElasticTransform","title":"class ElasticTransform (alpha=1, sigma=50, interpolation=1, border_mode=4, value=None, mask_value=None, approximate=False, same_dxdy=False, mask_interpolation=0, noise_distribution='gaussian', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformation to images, masks, bounding boxes, and keypoints.

This transformation introduces random elastic distortions to the input data. It's particularly useful for data augmentation in training deep learning models, especially for tasks like image segmentation or object detection where you want to maintain the relative positions of features while introducing realistic deformations.

The transform works by generating random displacement fields and applying them to the input. These fields are smoothed using a Gaussian filter to create more natural-looking distortions.

Parameters:

Name Type Description alpha float

Scaling factor for the random displacement fields. Higher values result in more pronounced distortions. Default: 1.0

sigma float

Standard deviation of the Gaussian filter used to smooth the displacement fields. Higher values result in smoother, more global distortions. Default: 50.0

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

approximate bool

Whether to use an approximate version of the elastic transform. If True, uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially less accurate for large sigma values. Default: False

same_dxdy bool

Whether to use the same random displacement field for both x and y directions. Can speed up the transform at the cost of less diverse distortions. Default: False

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

noise_distribution Literal[\"gaussian\", \"uniform\"]

Distribution used to generate the displacement fields. \"gaussian\" generates fields using normal distribution (more natural deformations). \"uniform\" generates fields using uniform distribution (more mechanical deformations). Default: \"gaussian\".

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints) by using the same displacement fields for all.
  • The 'approximate' parameter determines whether to use a precise or approximate method for generating displacement fields. The approximate method can be faster but may be less accurate for large sigma values.
  • Bounding boxes that end up outside the image after transformation will be removed.
  • Keypoints that end up outside the image after transformation will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ElasticTransform(BaseDistortion):\n    \"\"\"Apply elastic deformation to images, masks, bounding boxes, and keypoints.\n\n    This transformation introduces random elastic distortions to the input data. It's particularly\n    useful for data augmentation in training deep learning models, especially for tasks like\n    image segmentation or object detection where you want to maintain the relative positions of\n    features while introducing realistic deformations.\n\n    The transform works by generating random displacement fields and applying them to the input.\n    These fields are smoothed using a Gaussian filter to create more natural-looking distortions.\n\n    Args:\n        alpha (float): Scaling factor for the random displacement fields. Higher values result in\n            more pronounced distortions. Default: 1.0\n        sigma (float): Standard deviation of the Gaussian filter used to smooth the displacement\n            fields. Higher values result in smoother, more global distortions. Default: 50.0\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        approximate (bool): Whether to use an approximate version of the elastic transform. If True,\n            uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially\n            less accurate for large sigma values. Default: False\n        same_dxdy (bool): Whether to use the same random displacement field for both x and y\n            directions. Can speed up the transform at the cost of less diverse distortions. Default: False\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        noise_distribution (Literal[\"gaussian\", \"uniform\"]): Distribution used to generate the displacement fields.\n            \"gaussian\" generates fields using normal distribution (more natural deformations).\n            \"uniform\" generates fields using uniform distribution (more mechanical deformations).\n            Default: \"gaussian\".\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints)\n          by using the same displacement fields for all.\n        - The 'approximate' parameter determines whether to use a precise or approximate method for\n          generating displacement fields. The approximate method can be faster but may be less\n          accurate for large sigma values.\n        - Bounding boxes that end up outside the image after transformation will be removed.\n        - Keypoints that end up outside the image after transformation will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        alpha: Annotated[float, Field(ge=0)]\n        sigma: Annotated[float, Field(ge=1)]\n        approximate: bool\n        same_dxdy: bool\n        noise_distribution: Literal[\"gaussian\", \"uniform\"]\n        border_mode: BorderModeType = Field(deprecated=\"Deprecated\")\n        value: ColorType | None = Field(deprecated=\"Deprecated\")\n        mask_value: ColorType | None = Field(deprecated=\"Deprecated\")\n\n    def __init__(\n        self,\n        alpha: float = 1,\n        sigma: float = 50,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        approximate: bool = False,\n        same_dxdy: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        noise_distribution: Literal[\"gaussian\", \"uniform\"] = \"gaussian\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.alpha = alpha\n        self.sigma = sigma\n        self.approximate = approximate\n        self.same_dxdy = same_dxdy\n        self.noise_distribution = noise_distribution\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        kernel_size = (0, 0) if self.approximate else (17, 17)\n\n        # Generate displacement fields\n        dx, dy = fgeometric.generate_displacement_fields(\n            (height, width),\n            self.alpha,\n            self.sigma,\n            same_dxdy=self.same_dxdy,\n            kernel_size=kernel_size,\n            random_generator=self.random_generator,\n            noise_distribution=self.noise_distribution,\n        )\n\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        map_x = np.float32(x + dx)\n        map_y = np.float32(y + dy)\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"alpha\",\n            \"sigma\",\n            \"approximate\",\n            \"same_dxdy\",\n            \"noise_distribution\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.GridDistortion","title":"class GridDistortion (num_steps=5, distort_limit=(-0.3, 0.3), interpolation=1, border_mode=4, value=None, mask_value=None, normalized=True, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply grid distortion to images, masks, bounding boxes, and keypoints.

This transformation divides the image into a grid and randomly distorts each cell, creating localized warping effects. It's particularly useful for data augmentation in tasks like medical image analysis, OCR, and other domains where local geometric variations are meaningful.

Parameters:

Name Type Description num_steps int

Number of grid cells on each side of the image. Higher values create more granular distortions. Must be at least 1. Default: 5.

distort_limit float or tuple[float, float]

Range of distortion. If a single float is provided, the range will be (-distort_limit, distort_limit). Higher values create stronger distortions. Should be in the range of -1 to 1. Default: (-0.3, 0.3).

interpolation int

OpenCV interpolation method used for image transformation. Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.

normalized bool

If True, ensures that the distortion does not move pixels outside the image boundaries. This can result in less extreme distortions but guarantees that no information is lost. Default: True.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The same distortion is applied to all targets (image, mask, bboxes, keypoints) to maintain consistency.
  • When normalized=True, the distortion is adjusted to ensure all pixels remain within the image boundaries.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridDistortion(BaseDistortion):\n    \"\"\"Apply grid distortion to images, masks, bounding boxes, and keypoints.\n\n    This transformation divides the image into a grid and randomly distorts each cell,\n    creating localized warping effects. It's particularly useful for data augmentation\n    in tasks like medical image analysis, OCR, and other domains where local geometric\n    variations are meaningful.\n\n    Args:\n        num_steps (int): Number of grid cells on each side of the image. Higher values\n            create more granular distortions. Must be at least 1. Default: 5.\n        distort_limit (float or tuple[float, float]): Range of distortion. If a single float\n            is provided, the range will be (-distort_limit, distort_limit). Higher values\n            create stronger distortions. Should be in the range of -1 to 1.\n            Default: (-0.3, 0.3).\n        interpolation (int): OpenCV interpolation method used for image transformation.\n            Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.\n        normalized (bool): If True, ensures that the distortion does not move pixels\n            outside the image boundaries. This can result in less extreme distortions\n            but guarantees that no information is lost. Default: True.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The same distortion is applied to all targets (image, mask, bboxes, keypoints)\n          to maintain consistency.\n        - When normalized=True, the distortion is adjusted to ensure all pixels remain\n          within the image boundaries.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        num_steps: Annotated[int, Field(ge=1)]\n        distort_limit: SymmetricRangeType\n        normalized: bool\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int = Field(deprecated=\"Deprecated. Does not have any effect.\")\n\n        @field_validator(\"distort_limit\")\n        @classmethod\n        def check_limits(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = -1, 1\n            result = to_tuple(v)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        num_steps: int = 5,\n        distort_limit: ScaleFloatType = (-0.3, 0.3),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        normalized: bool = True,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.num_steps = num_steps\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.normalized = normalized\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        steps_x = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n        steps_y = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n\n        if self.normalized:\n            normalized_params = fgeometric.normalize_grid_distortion_steps(\n                image_shape,\n                self.num_steps,\n                steps_x,\n                steps_y,\n            )\n            steps_x, steps_y = (\n                normalized_params[\"steps_x\"],\n                normalized_params[\"steps_y\"],\n            )\n\n        map_x, map_y = fgeometric.generate_grid(\n            image_shape,\n            steps_x,\n            steps_y,\n            self.num_steps,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"num_steps\",\n            \"distort_limit\",\n            \"normalized\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.GridElasticDeform","title":"class GridElasticDeform (num_grid_xy, magnitude, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.

This transformation overlays a grid on the input and applies random displacements to the grid points, resulting in local elastic distortions. The granularity and intensity of the distortions can be controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.

Parameters:

Name Type Description num_grid_xy tuple[int, int]

Number of grid cells along the width and height. Specified as (grid_width, grid_height). Each value must be greater than 1.

magnitude int

Maximum pixel-wise displacement for distortion. Must be greater than 0.

interpolation int

Interpolation method to be used for the image transformation. Default: cv2.INTER_LINEAR

mask_interpolation int

Interpolation method to be used for mask transformation. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> transformed_image, transformed_mask = result['image'], result['mask']\n

Note

This transformation is particularly useful for data augmentation in medical imaging and other domains where elastic deformations can simulate realistic variations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridElasticDeform(DualTransform):\n    \"\"\"Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.\n\n    This transformation overlays a grid on the input and applies random displacements to the grid points,\n    resulting in local elastic distortions. The granularity and intensity of the distortions can be\n    controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.\n\n\n    Args:\n        num_grid_xy (tuple[int, int]): Number of grid cells along the width and height.\n            Specified as (grid_width, grid_height). Each value must be greater than 1.\n        magnitude (int): Maximum pixel-wise displacement for distortion. Must be greater than 0.\n        interpolation (int): Interpolation method to be used for the image transformation.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Interpolation method to be used for mask transformation.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = result['image'], result['mask']\n\n    Note:\n        This transformation is particularly useful for data augmentation in medical imaging\n        and other domains where elastic deformations can simulate realistic variations.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        num_grid_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        magnitude: int = Field(gt=0)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        num_grid_xy: tuple[int, int],\n        magnitude: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_grid_xy = num_grid_xy\n        self.magnitude = magnitude\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    @staticmethod\n    def generate_mesh(polygons: np.ndarray, dimensions: np.ndarray) -> np.ndarray:\n        return np.hstack((dimensions.reshape(-1, 4), polygons))\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        # Replace calculate_grid_dimensions with split_uniform_grid\n        tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.num_grid_xy,\n            self.random_generator,\n        )\n\n        # Convert tiles to the format expected by generate_distorted_grid_polygons\n        dimensions = np.array(\n            [\n                [\n                    tile[1],\n                    tile[0],\n                    tile[3],\n                    tile[2],\n                ]  # Reorder to [x_min, y_min, x_max, y_max]\n                for tile in tiles\n            ],\n        ).reshape(\n            self.num_grid_xy[::-1] + (4,),\n        )  # Reshape to (grid_height, grid_width, 4)\n\n        polygons = fgeometric.generate_distorted_grid_polygons(\n            dimensions,\n            self.magnitude,\n            self.random_generator,\n        )\n\n        generated_mesh = self.generate_mesh(polygons, dimensions)\n\n        return {\"generated_mesh\": generated_mesh}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(img, generated_mesh, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(mask, generated_mesh, self.mask_interpolation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        bboxes_denorm = denormalize_bboxes(bboxes, params[\"shape\"][:2])\n        return normalize_bboxes(\n            fgeometric.bbox_distort_image(\n                bboxes_denorm,\n                generated_mesh,\n                params[\"shape\"][:2],\n            ),\n            params[\"shape\"][:2],\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image_keypoints(\n            keypoints,\n            generated_mesh,\n            params[\"shape\"][:2],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_grid_xy\", \"magnitude\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.HorizontalFlip","title":"class HorizontalFlip [view source on GitHub]","text":"

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class HorizontalFlip(DualTransform):\n    \"\"\"Flip the input horizontally around the y-axis.\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return hflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_hflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_hflip(keypoints, params[\"shape\"][1])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.OpticalDistortion","title":"class OpticalDistortion (distort_limit=(-0.05, 0.05), shift_limit=None, interpolation=1, border_mode=None, value=None, mask_value=None, mask_interpolation=0, mode='camera', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply optical distortion to images, masks, bounding boxes, and keypoints.

Supports two distortion models: 1. Camera matrix model (original): Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients

  1. Fisheye model: Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)

Parameters:

Name Type Description distort_limit float | tuple[float, float]

Range of distortion coefficient. For camera model: recommended range (-0.05, 0.05) For fisheye model: recommended range (-0.3, 0.3) Default: (-0.05, 0.05)

mode Literal['camera', 'fisheye']

Distortion model to use: - 'camera': Original camera matrix model - 'fisheye': Fisheye lens model Default: 'camera'

interpolation OpenCV flag

Interpolation method used for image transformation. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.
  • The distortion coefficient (k) is randomly sampled from the distort_limit range.
  • The image center is shifted by dx and dy, randomly sampled from the shift_limit range.
  • Bounding boxes and keypoints are transformed along with the image to maintain consistency.
  • Fisheye model directly applies radial distortion
  • Both models use shift_limit to control distortion center

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class OpticalDistortion(BaseDistortion):\n    \"\"\"Apply optical distortion to images, masks, bounding boxes, and keypoints.\n\n    Supports two distortion models:\n    1. Camera matrix model (original):\n       Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients\n\n    2. Fisheye model:\n       Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)\n\n    Args:\n        distort_limit (float | tuple[float, float]): Range of distortion coefficient.\n            For camera model: recommended range (-0.05, 0.05)\n            For fisheye model: recommended range (-0.3, 0.3)\n            Default: (-0.05, 0.05)\n\n        mode (Literal['camera', 'fisheye']): Distortion model to use:\n            - 'camera': Original camera matrix model\n            - 'fisheye': Fisheye lens model\n            Default: 'camera'\n\n        interpolation (OpenCV flag): Interpolation method used for image transformation.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC,\n            cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.\n        - The distortion coefficient (k) is randomly sampled from the distort_limit range.\n        - The image center is shifted by dx and dy, randomly sampled from the shift_limit range.\n        - Bounding boxes and keypoints are transformed along with the image to maintain consistency.\n        - Fisheye model directly applies radial distortion\n        - Both models use shift_limit to control distortion center\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        distort_limit: SymmetricRangeType\n        mode: Literal[\"camera\", \"fisheye\"]\n        shift_limit: SymmetricRangeType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n    def __init__(\n        self,\n        distort_limit: ScaleFloatType = (-0.05, 0.05),\n        shift_limit: ScaleFloatType | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int | None = None,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        mode: Literal[\"camera\", \"fisheye\"] = \"camera\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.mode = mode\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        height, width = image_shape\n\n        # Get distortion coefficient\n        k = self.py_random.uniform(*self.distort_limit)\n\n        # Calculate center shift\n        center_xy = fgeometric.center(image_shape)\n\n        # Get distortion maps based on mode\n        if self.mode == \"camera\":\n            map_x, map_y = fgeometric.get_camera_matrix_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n        else:  # fisheye\n            map_x, map_y = fgeometric.get_fisheye_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"distort_limit\",\n            \"mode\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Pad","title":"class Pad (padding=0, fill=0, fill_mask=0, border_mode=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of an image by specified number of pixels.

Parameters:

Name Type Description padding int, tuple[int, int] or tuple[int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT

border_mode OpenCV flag

OpenCV border mode

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

References

  • https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Pad(DualTransform):\n    \"\"\"Pad the sides of an image by specified number of pixels.\n\n    Args:\n        padding (int, tuple[int, int] or tuple[int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y\n            * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT\n        fill_mask (ColorType): Padding value for mask if border_mode is cv2.BORDER_CONSTANT\n        border_mode (OpenCV flag): OpenCV border mode\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    References:\n        - https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        padding: int | tuple[int, int] | tuple[int, int, int, int]\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int] | tuple[int, int, int, int] = 0,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            img,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            mask,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill_mask,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_np = denormalize_bboxes(bboxes, params[\"shape\"])\n\n        result = fgeometric.pad_bboxes(\n            bboxes_np,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=image_shape,\n        )\n\n        rows, cols = params[\"shape\"][:2]\n        return normalize_bboxes(\n            result,\n            (rows + pad_top + pad_bottom, cols + pad_left + pad_right),\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_keypoints(\n            keypoints,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=params[\"shape\"][:2],\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if isinstance(self.padding, Real):\n            pad_top = pad_bottom = pad_left = pad_right = self.padding\n        elif isinstance(self.padding, (tuple, list)):\n            if len(self.padding) == NUM_PADS_XY:\n                pad_left = pad_right = self.padding[0]\n                pad_top = pad_bottom = self.padding[1]\n            elif len(self.padding) == NUM_PADS_ALL_SIDES:\n                pad_left, pad_top, pad_right, pad_bottom = self.padding  # type: ignore[misc]\n            else:\n                raise TypeError(\n                    \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n                )\n        else:\n            raise TypeError(\n                \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n            )\n\n        return {\n            \"pad_top\": pad_top,\n            \"pad_bottom\": pad_bottom,\n            \"pad_left\": pad_left,\n            \"pad_right\": pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"padding\",\n            \"fill\",\n            \"fill_mask\",\n            \"border_mode\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.PadIfNeeded","title":"class PadIfNeeded (min_height=1024, min_width=1024, pad_height_divisor=None, pad_width_divisor=None, position='center', border_mode=4, value=None, mask_value=None, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of an image if the image dimensions are less than the specified minimum dimensions. If the pad_height_divisor or pad_width_divisor is specified, the function additionally ensures that the image dimensions are divisible by these values.

Parameters:

Name Type Description min_height int | None

Minimum desired height of the image. Ensures image height is at least this value. If not specified, pad_height_divisor must be provided.

min_width int | None

Minimum desired width of the image. Ensures image width is at least this value. If not specified, pad_width_divisor must be provided.

pad_height_divisor int | None

If set, pads the image height to make it divisible by this value. If not specified, min_height must be provided.

pad_width_divisor int | None

If set, pads the image width to make it divisible by this value. If not specified, min_width must be provided.

position Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]

Position where the image is to be placed after padding. Default is 'center'.

border_mode int

Specifies the border mode to use if padding is required. The default is cv2.BORDER_REFLECT_101.

fill ColorType | None

Value to fill the border pixels if the border mode is cv2.BORDER_CONSTANT. Default is None.

fill_mask ColorType | None

Similar to fill but used for padding masks. Default is None.

p float

Probability of applying the transform. Default is 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • Either min_height or pad_height_divisor must be set, but not both.
  • Either min_width or pad_width_divisor must be set, but not both.
  • If border_mode is set to cv2.BORDER_CONSTANT, value must be provided.
  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).
  • For bounding boxes, the coordinates will be adjusted to account for the padding.
  • For keypoints, their positions will be shifted according to the padding.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> padded_image = transformed['image']\n>>> padded_mask = transformed['mask']\n>>> adjusted_bboxes = transformed['bboxes']\n>>> adjusted_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PadIfNeeded(Pad):\n    \"\"\"Pads the sides of an image if the image dimensions are less than the specified minimum dimensions.\n    If the `pad_height_divisor` or `pad_width_divisor` is specified, the function additionally ensures\n    that the image dimensions are divisible by these values.\n\n    Args:\n        min_height (int | None): Minimum desired height of the image. Ensures image height is at least this value.\n            If not specified, pad_height_divisor must be provided.\n        min_width (int | None): Minimum desired width of the image. Ensures image width is at least this value.\n            If not specified, pad_width_divisor must be provided.\n        pad_height_divisor (int | None): If set, pads the image height to make it divisible by this value.\n            If not specified, min_height must be provided.\n        pad_width_divisor (int | None): If set, pads the image width to make it divisible by this value.\n            If not specified, min_width must be provided.\n        position (Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]):\n            Position where the image is to be placed after padding. Default is 'center'.\n        border_mode (int): Specifies the border mode to use if padding is required.\n            The default is `cv2.BORDER_REFLECT_101`.\n        fill (ColorType | None): Value to fill the border pixels if the border mode is `cv2.BORDER_CONSTANT`.\n            Default is None.\n        fill_mask (ColorType | None): Similar to `fill` but used for padding masks. Default is None.\n        p (float): Probability of applying the transform. Default is 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Either `min_height` or `pad_height_divisor` must be set, but not both.\n        - Either `min_width` or `pad_width_divisor` must be set, but not both.\n        - If `border_mode` is set to `cv2.BORDER_CONSTANT`, `value` must be provided.\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).\n        - For bounding boxes, the coordinates will be adjusted to account for the padding.\n        - For keypoints, their positions will be shifted according to the padding.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> padded_image = transformed['image']\n        >>> padded_mask = transformed['mask']\n        >>> adjusted_bboxes = transformed['bboxes']\n        >>> adjusted_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        min_height: int | None = Field(ge=1)\n        min_width: int | None = Field(ge=1)\n        pad_height_divisor: int | None = Field(ge=1)\n        pad_width_divisor: int | None = Field(ge=1)\n        position: PositionType\n        border_mode: BorderModeType\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        @model_validator(mode=\"after\")\n        def validate_divisibility(self) -> Self:\n            if (self.min_height is None) == (self.pad_height_divisor is None):\n                msg = \"Only one of 'min_height' and 'pad_height_divisor' parameters must be set\"\n                raise ValueError(msg)\n            if (self.min_width is None) == (self.pad_width_divisor is None):\n                msg = \"Only one of 'min_width' and 'pad_width_divisor' parameters must be set\"\n                raise ValueError(msg)\n\n            if self.border_mode == cv2.BORDER_CONSTANT and self.fill is None:\n                msg = \"If 'border_mode' is set to 'BORDER_CONSTANT', 'fill' must be provided.\"\n                raise ValueError(msg)\n\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n\n            return self\n\n    def __init__(\n        self,\n        min_height: int | None = 1024,\n        min_width: int | None = 1024,\n        pad_height_divisor: int | None = None,\n        pad_width_divisor: int | None = None,\n        position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        # Initialize with dummy padding that will be calculated later\n        super().__init__(\n            padding=0,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            p=p,\n        )\n        self.min_height = min_height\n        self.min_width = min_width\n        self.pad_height_divisor = pad_height_divisor\n        self.pad_width_divisor = pad_width_divisor\n        self.position = position\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=params[\"shape\"][:2],\n            min_height=self.min_height,\n            min_width=self.min_width,\n            pad_height_divisor=self.pad_height_divisor,\n            pad_width_divisor=self.pad_width_divisor,\n        )\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_height\",\n            \"min_width\",\n            \"pad_height_divisor\",\n            \"pad_width_divisor\",\n            \"position\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Perspective","title":"class Perspective (scale=(0.05, 0.1), keep_size=True, pad_mode=None, pad_val=None, mask_pad_val=None, fit_output=False, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random four point perspective transformation to the input.

Parameters:

Name Type Description scale float or tuple of float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Default: (0.05, 0.1).

keep_size bool

Whether to resize image back to its original size after applying the perspective transform. If set to False, the resulting images may end up having different shapes. Default: True.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fit_output bool

If True, the image plane size and position will be adjusted to still capture the whole image after perspective transformation. This is followed by image resizing if keep_size is set to True. If False, parts of the transformed image may be outside of the image plane. This setting should not be set to True when using large scale values as it could lead to very large images. Default: False.

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

This transformation creates a perspective effect by randomly moving the four corners of the image. The amount of movement is controlled by the 'scale' parameter.

When 'keep_size' is True, the output image will have the same size as the input image, which may cause some parts of the transformed image to be cut off or padded.

When 'fit_output' is True, the transformation ensures that the entire transformed image is visible, which may result in a larger output image if keep_size is False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n... ])\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Perspective(DualTransform):\n    \"\"\"Apply random four point perspective transformation to the input.\n\n    Args:\n        scale (float or tuple of float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Default: (0.05, 0.1).\n        keep_size (bool): Whether to resize image back to its original size after applying the perspective transform.\n            If set to False, the resulting images may end up having different shapes.\n            Default: True.\n        border_mode (OpenCV flag): OpenCV border mode used for padding.\n            Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n        fill_mask (ColorType): Padding value for mask if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fit_output (bool): If True, the image plane size and position will be adjusted to still capture\n            the whole image after perspective transformation. This is followed by image resizing if keep_size is set\n            to True. If False, parts of the transformed image may be outside of the image plane.\n            This setting should not be set to True when using large scale values as it could lead to very large images.\n            Default: False.\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transformation creates a perspective effect by randomly moving the four corners of the image.\n        The amount of movement is controlled by the 'scale' parameter.\n\n        When 'keep_size' is True, the output image will have the same size as the input image,\n        which may cause some parts of the transformed image to be cut off or padded.\n\n        When 'fit_output' is True, the transformation ensures that the entire transformed image is visible,\n        which may result in a larger output image if keep_size is False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n        ... ])\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        keep_size: bool\n        pad_mode: BorderModeType | None = Field(\n            deprecated=\"Deprecated use border_mode instead\",\n        )\n        pad_val: ColorType | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_pad_val: ColorType | None = Field(\n            deprecated=\"Deprecated use fill_mask instead\",\n        )\n        fit_output: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def validate_deprecated_fields(self) -> Self:\n            if self.pad_mode is not None:\n                self.border_mode = self.pad_mode\n            if self.pad_val is not None:\n                self.fill = self.pad_val\n            if self.mask_pad_val is not None:\n                self.fill_mask = self.mask_pad_val\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.05, 0.1),\n        keep_size: bool = True,\n        pad_mode: int | None = None,\n        pad_val: ColorType | None = None,\n        mask_pad_val: ColorType | None = None,\n        fit_output: bool = False,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply=always_apply)\n        self.scale = cast(tuple[float, float], scale)\n        self.keep_size = keep_size\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.fit_output = fit_output\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            img,\n            matrix,\n            max_width,\n            max_height,\n            self.fill,\n            self.border_mode,\n            self.keep_size,\n            self.interpolation,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            mask,\n            matrix,\n            max_width,\n            max_height,\n            self.fill_mask,\n            self.border_mode,\n            self.keep_size,\n            self.mask_interpolation,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        matrix_bbox: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_bboxes(\n            bboxes,\n            params[\"shape\"],\n            matrix_bbox,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_keypoints(\n            keypoints,\n            params[\"shape\"],\n            matrix,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        scale = self.py_random.uniform(*self.scale)\n\n        points = fgeometric.generate_perspective_points(\n            image_shape,\n            scale,\n            self.random_generator,\n        )\n        points = fgeometric.order_points(points)\n\n        matrix, max_width, max_height = fgeometric.compute_perspective_params(\n            points,\n            image_shape,\n        )\n\n        if self.fit_output:\n            matrix, max_width, max_height = fgeometric.expand_transform(\n                matrix,\n                image_shape,\n            )\n\n        return {\n            \"matrix\": matrix,\n            \"max_height\": max_height,\n            \"max_width\": max_width,\n            \"matrix_bbox\": matrix,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"keep_size\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"fit_output\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.PiecewiseAffine","title":"class PiecewiseAffine (scale=(0.03, 0.05), nb_rows=(4, 4), nb_cols=(4, 4), interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, absolute_scale=False, p=0.5, always_apply=None, keypoints_threshold=0.01) [view source on GitHub]","text":"

Apply piecewise affine transformations to the input image.

This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points around via affine transformations. This leads to local distortions in the image.

Parameters:

Name Type Description scale tuple[float, float] | float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Recommended values are in the range (0.01, 0.05) for small distortions, and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).

nb_rows tuple[int, int] | int

Number of rows of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of rows. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

nb_cols tuple[int, int] | int

Number of columns of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of columns. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

absolute_scale bool

If set to True, the value of the scale parameter will be treated as an absolute pixel value. If set to False, it will be treated as a fraction of the image height and width. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This augmentation is very slow. Consider using ElasticTransform instead, which is at least 10x faster.
  • The augmentation may not always produce visible effects, especially with small scale values.
  • For keypoints and bounding boxes, the transformation might move them outside the image boundaries. In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PiecewiseAffine(BaseDistortion):\n    \"\"\"Apply piecewise affine transformations to the input image.\n\n    This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points\n    around via affine transformations. This leads to local distortions in the image.\n\n    Args:\n        scale (tuple[float, float] | float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Recommended values are in the range (0.01, 0.05) for small distortions,\n            and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).\n        nb_rows (tuple[int, int] | int): Number of rows of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of rows.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        nb_cols (tuple[int, int] | int): Number of columns of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of columns.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        absolute_scale (bool): If set to True, the value of the scale parameter will be treated as an absolute\n            pixel value. If set to False, it will be treated as a fraction of the image height and width.\n            Default: False.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This augmentation is very slow. Consider using `ElasticTransform` instead, which is at least 10x faster.\n        - The augmentation may not always produce visible effects, especially with small scale values.\n        - For keypoints and bounding boxes, the transformation might move them outside the image boundaries.\n          In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed[\"image\"]\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        nb_rows: ScaleIntType\n        nb_cols: ScaleIntType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        cval: int | None = Field(deprecated=\"Deprecated. Does not have any effect.\")\n        cval_mask: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = Field(\n            deprecated=\"Deprecated. Does not have any effects.\",\n        )\n\n        absolute_scale: bool\n        keypoints_threshold: float = Field(\n            deprecated=\"This parameter is not used anymore\",\n        )\n\n        @field_validator(\"nb_rows\", \"nb_cols\")\n        @classmethod\n        def process_range(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 2, BIG_INTEGER\n            result = to_tuple(value, value)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.03, 0.05),\n        nb_rows: ScaleIntType = (4, 4),\n        nb_cols: ScaleIntType = (4, 4),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: int | None = None,\n        cval_mask: int | None = None,\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = None,\n        absolute_scale: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n        keypoints_threshold: float = 0.01,\n    ):\n        super().__init__(\n            p=p,\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n        )\n\n        warn(\n            \"This augmenter is very slow. Try to use ``ElasticTransform`` instead, which is at least 10x faster.\",\n            stacklevel=2,\n        )\n\n        self.scale = cast(tuple[float, float], scale)\n        self.nb_rows = cast(tuple[int, int], nb_rows)\n        self.nb_cols = cast(tuple[int, int], nb_cols)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.absolute_scale = absolute_scale\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"nb_rows\",\n            \"nb_cols\",\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"absolute_scale\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        nb_rows = np.clip(self.py_random.randint(*self.nb_rows), 2, None)\n        nb_cols = np.clip(self.py_random.randint(*self.nb_cols), 2, None)\n        scale = self.py_random.uniform(*self.scale)\n\n        map_x, map_y = fgeometric.create_piecewise_affine_maps(\n            image_shape=image_shape,\n            grid=(nb_rows, nb_cols),\n            scale=scale,\n            absolute_scale=self.absolute_scale,\n            random_generator=self.random_generator,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.RandomGridShuffle","title":"class RandomGridShuffle (grid=(3, 3), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shuffles the grid's cells on an image, mask, or keypoints, effectively rearranging patches within the image. This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.

Parameters:

Name Type Description grid tuple[int, int]

Size of the grid for splitting the image into cells. Each cell is shuffled randomly. For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled. Default: (3, 3)

p float

Probability that the transform will be applied. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This transform maintains consistency across all targets. If applied to an image and its corresponding mask or keypoints, the same shuffling will be applied to all.
  • The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2)) for the transform to have any effect.
  • Keypoints are moved along with their corresponding grid cell.
  • This transform could be useful when only micro features are important for the model, and memorizing the global structure could be harmful. For example:
  • Identifying the type of cell phone used to take a picture based on micro artifacts generated by phone post-processing algorithms, rather than the semantic features of the photo. See more at https://ieeexplore.ieee.org/abstract/document/8622031
  • Identifying stress, glucose, hydration levels based on skin images.

Mathematical Formulation: 1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter. 2. A random permutation P of integers from 0 to (mn - 1) is generated. 3. Each cell in the grid is assigned a number from 0 to (mn - 1) in row-major order. 4. The cells are then rearranged according to the permutation P.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4]\n... ])\n>>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image might look like this (one possible outcome):\n# [[4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1]]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class RandomGridShuffle(DualTransform):\n    \"\"\"Randomly shuffles the grid's cells on an image, mask, or keypoints,\n    effectively rearranging patches within the image.\n    This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.\n\n    Args:\n        grid (tuple[int, int]): Size of the grid for splitting the image into cells. Each cell is shuffled randomly.\n            For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled.\n            Default: (3, 3)\n        p (float): Probability that the transform will be applied. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform maintains consistency across all targets. If applied to an image and its corresponding\n          mask or keypoints, the same shuffling will be applied to all.\n        - The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2))\n          for the transform to have any effect.\n        - Keypoints are moved along with their corresponding grid cell.\n        - This transform could be useful when only micro features are important for the model, and memorizing\n          the global structure could be harmful. For example:\n          - Identifying the type of cell phone used to take a picture based on micro artifacts generated by\n            phone post-processing algorithms, rather than the semantic features of the photo.\n            See more at https://ieeexplore.ieee.org/abstract/document/8622031\n          - Identifying stress, glucose, hydration levels based on skin images.\n\n    Mathematical Formulation:\n        1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter.\n        2. A random permutation P of integers from 0 to (m*n - 1) is generated.\n        3. Each cell in the grid is assigned a number from 0 to (m*n - 1) in row-major order.\n        4. The cells are then rearranged according to the permutation P.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4]\n        ... ])\n        >>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image might look like this (one possible outcome):\n        # [[4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1]]\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        grid: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        grid: tuple[int, int] = (3, 3),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.grid = grid\n\n    def apply(\n        self,\n        img: np.ndarray,\n        tiles: np.ndarray,\n        mapping: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_image(img, tiles, mapping)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n        bboxes_returned = fgeometric.bboxes_grid_shuffle(\n            bboxes_denorm,\n            tiles,\n            mapping,\n            image_shape,\n            min_area=processor.params.min_area,\n            min_visibility=processor.params.min_visibility,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_keypoints(keypoints, tiles, mapping)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        original_tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.grid,\n            self.random_generator,\n        )\n        shape_groups = fgeometric.create_shape_groups(original_tiles)\n        mapping = fgeometric.shuffle_tiles_within_shape_groups(\n            shape_groups,\n            self.random_generator,\n        )\n\n        return {\"tiles\": original_tiles, \"mapping\": mapping}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"grid\",)\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ShiftScaleRotate","title":"class ShiftScaleRotate (shift_limit=(-0.0625, 0.0625), scale_limit=(-0.1, 0.1), rotate_limit=(-45, 45), interpolation=1, border_mode=4, value=None, mask_value=None, shift_limit_x=None, shift_limit_y=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly apply affine transforms: translate, scale and rotate the input.

Parameters:

Name Type Description shift_limit float, float) or float

shift factor range for both height and width. If shift_limit is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).

scale_limit float, float) or float

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

rotate_limit int, int) or int

rotation range. If rotate_limit is a single int value, the range will be (-rotate_limit, rotate_limit). Default: (-45, 45).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

shift_limit_x float, float) or float

shift factor range for width. If it is set then this value instead of shift_limit will be used for shifting width. If shift_limit_x is a single float value, the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in the range [-1, 1]. Default: None.

shift_limit_y float, float) or float

shift factor range for height. If it is set then this value instead of shift_limit will be used for shifting height. If shift_limit_y is a single float value, the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie in the range [-, 1]. Default: None.

rotate_method str

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\". Default: \"largest_box\"

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ShiftScaleRotate(Affine):\n    \"\"\"Randomly apply affine transforms: translate, scale and rotate the input.\n\n    Args:\n        shift_limit ((float, float) or float): shift factor range for both height and width. If shift_limit\n            is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and\n            upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).\n        scale_limit ((float, float) or float): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        rotate_limit ((int, int) or int): rotation range. If rotate_limit is a single int value, the\n            range will be (-rotate_limit, rotate_limit). Default: (-45, 45).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        shift_limit_x ((float, float) or float): shift factor range for width. If it is set then this value\n            instead of shift_limit will be used for shifting width.  If shift_limit_x is a single float value,\n            the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in\n            the range [-1, 1]. Default: None.\n        shift_limit_y ((float, float) or float): shift factor range for height. If it is set then this value\n            instead of shift_limit will be used for shifting height.  If shift_limit_y is a single float value,\n            the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie\n            in the range [-, 1]. Default: None.\n        rotate_method (str): rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\".\n            Default: \"largest_box\"\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        shift_limit: SymmetricRangeType = (-0.0625, 0.0625)\n        scale_limit: SymmetricRangeType = (-0.1, 0.1)\n        rotate_limit: SymmetricRangeType = (-45, 45)\n        interpolation: InterpolationType = cv2.INTER_LINEAR\n        border_mode: BorderModeType = cv2.BORDER_REFLECT_101\n\n        value: ColorType | None = Field(\n            default=None,\n            deprecated=\"Deprecated. Use fill instead.\",\n        )\n        mask_value: ColorType | None = Field(\n            default=None,\n            deprecated=\"Deprecated. Use fill_mask instead.\",\n        )\n\n        fill: ColorType = 0\n        fill_mask: ColorType = 0\n\n        shift_limit_x: ScaleFloatType | None = Field(default=None)\n        shift_limit_y: ScaleFloatType | None = Field(default=None)\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\"\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def check_shift_limit(self) -> Self:\n            bounds = -1, 1\n            self.shift_limit_x = to_tuple(\n                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_x, *bounds, \"shift_limit_x\")\n            self.shift_limit_y = to_tuple(\n                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_y, *bounds, \"shift_limit_y\")\n\n            return self\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> ScaleFloatType:\n            bounds = 0, float(\"inf\")\n            result = to_tuple(value, bias=1.0)\n            check_range(result, *bounds, str(info.field_name))\n            return result\n\n    def __init__(\n        self,\n        shift_limit: ScaleFloatType = (-0.0625, 0.0625),\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        rotate_limit: ScaleFloatType = (-45, 45),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        shift_limit_x: ScaleFloatType | None = None,\n        shift_limit_y: ScaleFloatType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: InterpolationType = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        shift_limit_x = cast(tuple[float, float], shift_limit_x)\n        shift_limit_y = cast(tuple[float, float], shift_limit_y)\n        super().__init__(\n            scale=scale_limit,\n            translate_percent={\"x\": shift_limit_x, \"y\": shift_limit_y},\n            rotate=rotate_limit,\n            shear=(0, 0),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            fit_output=False,\n            keep_ratio=False,\n            rotate_method=rotate_method,\n            always_apply=always_apply,\n            p=p,\n        )\n        warn(\n            \"ShiftScaleRotate is deprecated. Please use Affine transform instead.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        self.shift_limit_x = shift_limit_x\n        self.shift_limit_y = shift_limit_y\n\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"shift_limit_x\": self.shift_limit_x,\n            \"shift_limit_y\": self.shift_limit_y,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n            \"rotate_limit\": self.rotate_limit,\n            \"interpolation\": self.interpolation,\n            \"border_mode\": self.border_mode,\n            \"fill\": self.fill,\n            \"fill_mask\": self.fill_mask,\n            \"rotate_method\": self.rotate_method,\n            \"mask_interpolation\": self.mask_interpolation,\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ThinPlateSpline","title":"class ThinPlateSpline (scale_range=(0.2, 0.4), num_control_points=4, interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.

Imagine the image printed on a thin metal plate that can be bent and warped smoothly: - Control points act like pins pushing or pulling the plate - The plate resists sharp bending, creating smooth deformations - The transformation maintains continuity (no tears or folds) - Areas between control points are interpolated naturally

The transform works by: 1. Creating a regular grid of control points (like pins in the plate) 2. Randomly displacing these points (like pushing/pulling the pins) 3. Computing a smooth interpolation (like the plate bending) 4. Applying the resulting deformation to the image

Parameters:

Name Type Description scale_range tuple[float, float]

Range for random displacement of control points. Values should be in [0.0, 1.0]: - 0.0: No displacement (identity transform) - 0.1: Subtle warping - 0.2-0.4: Moderate deformation (recommended range) - 0.5+: Strong warping Default: (0.2, 0.4)

num_control_points int

Number of control points per side. Creates a grid of num_control_points x num_control_points points. - 2: Minimal deformation (affine-like) - 3-4: Moderate flexibility (recommended) - 5+: More local deformation control Must be >= 2. Default: 4

interpolation int

OpenCV interpolation flag. Used for image sampling. See also: cv2.INTER_* Default: cv2.INTER_LINEAR

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • The transformation preserves smoothness and continuity
  • Stronger scale values may create more extreme deformations
  • Higher number of control points allows more local deformations
  • The same deformation is applied consistently to all targets

Examples:

Python
>>> import albumentations as A\n>>> # Basic usage\n>>> transform = A.ThinPlateSpline()\n>>>\n>>> # Subtle deformation\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.1, 0.2),\n...     num_control_points=3\n... )\n>>>\n>>> # Strong warping with fine control\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.3, 0.5),\n...     num_control_points=5,\n... )\n

References

  • \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\" by F.L. Bookstein https://doi.org/10.1109/34.24792

  • Thin Plate Splines in Computer Vision: https://en.wikipedia.org/wiki/Thin_plate_spline

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline

See Also: - ElasticTransform: For different type of non-rigid deformation - GridDistortion: For grid-based warping - OpticalDistortion: For lens-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ThinPlateSpline(BaseDistortion):\n    r\"\"\"Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.\n\n    Imagine the image printed on a thin metal plate that can be bent and warped smoothly:\n    - Control points act like pins pushing or pulling the plate\n    - The plate resists sharp bending, creating smooth deformations\n    - The transformation maintains continuity (no tears or folds)\n    - Areas between control points are interpolated naturally\n\n    The transform works by:\n    1. Creating a regular grid of control points (like pins in the plate)\n    2. Randomly displacing these points (like pushing/pulling the pins)\n    3. Computing a smooth interpolation (like the plate bending)\n    4. Applying the resulting deformation to the image\n\n\n    Args:\n        scale_range (tuple[float, float]): Range for random displacement of control points.\n            Values should be in [0.0, 1.0]:\n            - 0.0: No displacement (identity transform)\n            - 0.1: Subtle warping\n            - 0.2-0.4: Moderate deformation (recommended range)\n            - 0.5+: Strong warping\n            Default: (0.2, 0.4)\n\n        num_control_points (int): Number of control points per side.\n            Creates a grid of num_control_points x num_control_points points.\n            - 2: Minimal deformation (affine-like)\n            - 3-4: Moderate flexibility (recommended)\n            - 5+: More local deformation control\n            Must be >= 2. Default: 4\n\n        interpolation (int): OpenCV interpolation flag. Used for image sampling.\n            See also: cv2.INTER_*\n            Default: cv2.INTER_LINEAR\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transformation preserves smoothness and continuity\n        - Stronger scale values may create more extreme deformations\n        - Higher number of control points allows more local deformations\n        - The same deformation is applied consistently to all targets\n\n    Example:\n        >>> import albumentations as A\n        >>> # Basic usage\n        >>> transform = A.ThinPlateSpline()\n        >>>\n        >>> # Subtle deformation\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.1, 0.2),\n        ...     num_control_points=3\n        ... )\n        >>>\n        >>> # Strong warping with fine control\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.3, 0.5),\n        ...     num_control_points=5,\n        ... )\n\n    References:\n        - \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\"\n          by F.L. Bookstein\n          https://doi.org/10.1109/34.24792\n\n        - Thin Plate Splines in Computer Vision:\n          https://en.wikipedia.org/wiki/Thin_plate_spline\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline\n\n    See Also:\n        - ElasticTransform: For different type of non-rigid deformation\n        - GridDistortion: For grid-based warping\n        - OpticalDistortion: For lens-like distortions\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        scale_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        num_control_points: int = Field(ge=2)\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.2, 0.4),\n        num_control_points: int = 4,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale_range = scale_range\n        self.num_control_points = num_control_points\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        # Create regular grid of control points\n        grid_size = self.num_control_points\n        x = np.linspace(0, 1, grid_size)\n        y = np.linspace(0, 1, grid_size)\n        src_points = np.stack(np.meshgrid(x, y), axis=-1).reshape(-1, 2)\n\n        # Add random displacement to destination points\n        scale = self.py_random.uniform(*self.scale_range) / 10\n        dst_points = src_points + self.random_generator.normal(\n            0,\n            scale,\n            src_points.shape,\n        )\n\n        # Compute TPS weights\n        weights, affine = fgeometric.compute_tps_weights(src_points, dst_points)\n\n        # Create grid of points\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        points = np.stack([x.flatten(), y.flatten()], axis=1).astype(np.float32)\n\n        # Transform points\n        transformed = fgeometric.tps_transform(\n            points / [width, height],\n            src_points,\n            weights,\n            affine,\n        )\n        transformed *= [width, height]\n\n        return {\n            \"map_x\": transformed[:, 0].reshape(height, width).astype(np.float32),\n            \"map_y\": transformed[:, 1].reshape(height, width).astype(np.float32),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale_range\",\n            \"num_control_points\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Transpose","title":"class Transpose [view source on GitHub]","text":"

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.Transpose(p=1.0)\n>>> result = transform(image=image)\n>>> transposed_image = result['image']\n>>> print(transposed_image)\n[[[ 1  2  3]\n  [ 7  8  9]]\n [[ 4  5  6]\n  [10 11 12]]]\n# The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Transpose(DualTransform):\n    \"\"\"Transpose the input by swapping its rows and columns.\n\n    This transform flips the image over its main diagonal, effectively switching its width and height.\n    It's equivalent to a 90-degree rotation followed by a horizontal flip.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The dimensions of the output will be swapped compared to the input. For example,\n          an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).\n        - This transform is its own inverse. Applying it twice will return the original input.\n        - For multi-channel images (like RGB), the channels are preserved in their original order.\n        - Bounding boxes will have their coordinates adjusted to match the new image dimensions.\n        - Keypoints will have their x and y coordinates swapped.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (y_min, x_min, y_max, x_max)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (y, x)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.Transpose(p=1.0)\n        >>> result = transform(image=image)\n        >>> transposed_image = result['image']\n        >>> print(transposed_image)\n        [[[ 1  2  3]\n          [ 7  8  9]]\n         [[ 4  5  6]\n          [10 11 12]]]\n        # The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.transpose(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_transpose(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_transpose(keypoints)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.VerticalFlip","title":"class VerticalFlip [view source on GitHub]","text":"

Flip the input vertically around the x-axis.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.VerticalFlip(p=1.0)\n>>> result = transform(image=image)\n>>> flipped_image = result['image']\n>>> print(flipped_image)\n[[[ 7  8  9]\n  [10 11 12]]\n [[ 1  2  3]\n  [ 4  5  6]]]\n# The original image is flipped vertically, with rows reversed\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class VerticalFlip(DualTransform):\n    \"\"\"Flip the input vertically around the x-axis.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform flips the image upside down. The top of the image becomes the bottom and vice versa.\n        - The dimensions of the image remain unchanged.\n        - For multi-channel images (like RGB), each channel is flipped independently.\n        - Bounding boxes are adjusted to match their new positions in the flipped image.\n        - Keypoints are moved to their new positions in the flipped image.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (x_min, H-y_max, x_max, H-y_min)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (x, H-y)\n        where H is the height of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.VerticalFlip(p=1.0)\n        >>> result = transform(image=image)\n        >>> flipped_image = result['image']\n        >>> print(flipped_image)\n        [[[ 7  8  9]\n          [10 11 12]]\n         [[ 1  2  3]\n          [ 4  5  6]]]\n        # The original image is flipped vertically, with rows reversed\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return vflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_vflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_vflip(keypoints, params[\"shape\"][0])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/mixing/","title":"Index","text":"
  • Mixing transforms (augmentations.mixing.transforms)
  • Mixing functional transforms (albumentations.augmentations.mixing.functional)
"},{"location":"api_reference/augmentations/mixing/functional/","title":"Mixing transforms (augmentations.mixing.functional)","text":""},{"location":"api_reference/augmentations/mixing/transforms/","title":"Mixing transforms (augmentations.mixing.transforms)","text":""},{"location":"api_reference/augmentations/mixing/transforms/#albumentations.augmentations.mixing.transforms.OverlayElements","title":"class OverlayElements (metadata_key='overlay_metadata', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply overlay elements such as images and masks onto an input image. This transformation can be used to add various objects (e.g., stickers, logos) to images with optional masks and bounding boxes for better placement control.

Parameters:

Name Type Description metadata_key str

Additional target key for metadata. Default overlay_metadata.

p float

Probability of applying the transformation. Default: 0.5.

Possible Metadata Fields: - image (np.ndarray): The overlay image to be applied. This is a required field. - bbox (list[int]): The bounding box specifying the region where the overlay should be applied. It should contain four floats: [y_min, x_min, y_max, x_max]. If label_id is provided, it should be appended as the fifth element in the bbox. BBox should be in Albumentations format, that is the same as normalized Pascal VOC format [x_min / width, y_min / height, x_max / width, y_max / height] - mask (np.ndarray): An optional mask that defines the non-rectangular region of the overlay image. If not provided, the entire overlay image is used. - mask_id (int): An optional identifier for the mask. If provided, the regions specified by the mask will be labeled with this identifier in the output mask.

Targets

image, mask

Image types: uint8, float32

Reference

https://github.com/danaaubakirova/doc-augmentation

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/mixing/transforms.py Python
class OverlayElements(DualTransform):\n    \"\"\"Apply overlay elements such as images and masks onto an input image. This transformation can be used to add\n    various objects (e.g., stickers, logos) to images with optional masks and bounding boxes for better placement\n    control.\n\n    Args:\n        metadata_key (str): Additional target key for metadata. Default `overlay_metadata`.\n        p (float): Probability of applying the transformation. Default: 0.5.\n\n    Possible Metadata Fields:\n        - image (np.ndarray): The overlay image to be applied. This is a required field.\n        - bbox (list[int]): The bounding box specifying the region where the overlay should be applied. It should\n                            contain four floats: [y_min, x_min, y_max, x_max]. If `label_id` is provided, it should\n                            be appended as the fifth element in the bbox. BBox should be in Albumentations format,\n                            that is the same as normalized Pascal VOC format\n                            [x_min / width, y_min / height, x_max / width, y_max / height]\n        - mask (np.ndarray): An optional mask that defines the non-rectangular region of the overlay image. If not\n                             provided, the entire overlay image is used.\n        - mask_id (int): An optional identifier for the mask. If provided, the regions specified by the mask will\n                         be labeled with this identifier in the output mask.\n\n    Targets:\n        image, mask\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://github.com/danaaubakirova/doc-augmentation\n\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    class InitSchema(BaseTransformInitSchema):\n        metadata_key: str\n\n    def __init__(\n        self,\n        metadata_key: str = \"overlay_metadata\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.metadata_key = metadata_key\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [self.metadata_key]\n\n    @staticmethod\n    def preprocess_metadata(\n        metadata: dict[str, Any],\n        img_shape: tuple[int, int],\n        random_state: random.Random,\n    ) -> dict[str, Any]:\n        overlay_image = metadata[\"image\"]\n        overlay_height, overlay_width = overlay_image.shape[:2]\n        image_height, image_width = img_shape[:2]\n\n        if \"bbox\" in metadata:\n            bbox = metadata[\"bbox\"]\n            bbox_np = np.array([bbox])\n            check_bboxes(bbox_np)\n            denormalized_bbox = denormalize_bboxes(bbox_np, img_shape[:2])[0]\n\n            x_min, y_min, x_max, y_max = (int(x) for x in denormalized_bbox[:4])\n\n            if \"mask\" in metadata:\n                mask = metadata[\"mask\"]\n                mask = cv2.resize(mask, (x_max - x_min, y_max - y_min), interpolation=cv2.INTER_NEAREST)\n            else:\n                mask = np.ones((y_max - y_min, x_max - x_min), dtype=np.uint8)\n\n            overlay_image = cv2.resize(overlay_image, (x_max - x_min, y_max - y_min), interpolation=cv2.INTER_AREA)\n            offset = (y_min, x_min)\n\n            if len(bbox) == LENGTH_RAW_BBOX and \"bbox_id\" in metadata:\n                bbox = [x_min, y_min, x_max, y_max, metadata[\"bbox_id\"]]\n            else:\n                bbox = (x_min, y_min, x_max, y_max, *bbox[4:])\n        else:\n            if image_height < overlay_height or image_width < overlay_width:\n                overlay_image = cv2.resize(overlay_image, (image_width, image_height), interpolation=cv2.INTER_AREA)\n                overlay_height, overlay_width = overlay_image.shape[:2]\n\n            mask = metadata[\"mask\"] if \"mask\" in metadata else np.ones_like(overlay_image, dtype=np.uint8)\n\n            max_x_offset = image_width - overlay_width\n            max_y_offset = image_height - overlay_height\n\n            offset_x = random_state.randint(0, max_x_offset)\n            offset_y = random_state.randint(0, max_y_offset)\n\n            offset = (offset_y, offset_x)\n\n            bbox = [\n                offset_x,\n                offset_y,\n                offset_x + overlay_width,\n                offset_y + overlay_height,\n            ]\n\n            if \"bbox_id\" in metadata:\n                bbox = [*bbox, metadata[\"bbox_id\"]]\n\n        result = {\n            \"overlay_image\": overlay_image,\n            \"overlay_mask\": mask,\n            \"offset\": offset,\n            \"bbox\": bbox,\n        }\n\n        if \"mask_id\" in metadata:\n            result[\"mask_id\"] = metadata[\"mask_id\"]\n\n        return result\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        metadata = data[self.metadata_key]\n        img_shape = params[\"shape\"]\n\n        if isinstance(metadata, list):\n            overlay_data = [self.preprocess_metadata(md, img_shape, self.py_random) for md in metadata]\n        else:\n            overlay_data = [self.preprocess_metadata(metadata, img_shape, self.py_random)]\n\n        return {\n            \"overlay_data\": overlay_data,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        overlay_data: list[dict[str, Any]],\n        **params: Any,\n    ) -> np.ndarray:\n        for data in overlay_data:\n            overlay_image = data[\"overlay_image\"]\n            overlay_mask = data[\"overlay_mask\"]\n            offset = data[\"offset\"]\n            img = fmixing.copy_and_paste_blend(img, overlay_image, overlay_mask, offset=offset)\n        return img\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        overlay_data: list[dict[str, Any]],\n        **params: Any,\n    ) -> np.ndarray:\n        for data in overlay_data:\n            if \"mask_id\" in data and data[\"mask_id\"] is not None:\n                overlay_mask = data[\"overlay_mask\"]\n                offset = data[\"offset\"]\n                mask_id = data[\"mask_id\"]\n\n                y_min, x_min = offset\n                y_max = y_min + overlay_mask.shape[0]\n                x_max = x_min + overlay_mask.shape[1]\n\n                mask_section = mask[y_min:y_max, x_min:x_max]\n                mask_section[overlay_mask > 0] = mask_id\n\n        return mask\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"metadata_key\",)\n
"},{"location":"api_reference/augmentations/transforms3d/","title":"Index","text":"
  • 3D (Volumetric) transforms (augmentations.transforms3d.transforms)
  • 3D (Volumetric) functional transforms (albumentations.augmentations.transforms3d.functional)
"},{"location":"api_reference/augmentations/transforms3d/functional/","title":"3D (Volumetric) functional transforms (augmentations.transforms3d.functional)","text":""},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.adjust_padding_by_position3d","title":"def adjust_padding_by_position3d (paddings, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position for 3D data.

Parameters:

Name Type Description paddings list[tuple[int, int]]

List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]

position Literal['center', 'random']

Position of the image after padding. Either 'center' or 'random'

py_random Random

Random number generator

Returns:

Type Description tuple[int, int, int, int, int, int]

Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

Source code in albumentations/augmentations/transforms3d/functional.py Python
def adjust_padding_by_position3d(\n    paddings: list[tuple[int, int]],  # [(front, back), (top, bottom), (left, right)]\n    position: Literal[\"center\", \"random\"],\n    py_random: random.Random,\n) -> tuple[int, int, int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position for 3D data.\n\n    Args:\n        paddings: List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]\n        position: Position of the image after padding. Either 'center' or 'random'\n        py_random: Random number generator\n\n    Returns:\n        tuple[int, int, int, int, int, int]: Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)\n    \"\"\"\n    if position == \"center\":\n        return (\n            paddings[0][0],  # d_front\n            paddings[0][1],  # d_back\n            paddings[1][0],  # h_top\n            paddings[1][1],  # h_bottom\n            paddings[2][0],  # w_left\n            paddings[2][1],  # w_right\n        )\n\n    # For random position, redistribute padding for each dimension\n    d_pad = sum(paddings[0])\n    h_pad = sum(paddings[1])\n    w_pad = sum(paddings[2])\n\n    return (\n        py_random.randint(0, d_pad),  # d_front\n        d_pad - py_random.randint(0, d_pad),  # d_back\n        py_random.randint(0, h_pad),  # h_top\n        h_pad - py_random.randint(0, h_pad),  # h_bottom\n        py_random.randint(0, w_pad),  # w_left\n        w_pad - py_random.randint(0, w_pad),  # w_right\n    )\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.crop3d","title":"def crop3d (volume, crop_coords) [view source on GitHub]","text":"

Crop 3D volume using coordinates.

Parameters:

Name Type Description volume ndarray

Input volume with shape (z, y, x) or (z, y, x, channels)

crop_coords tuple[int, int, int, int, int, int]

Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping

Returns:

Type Description ndarray

Cropped volume with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py Python
def crop3d(\n    volume: np.ndarray,\n    crop_coords: tuple[int, int, int, int, int, int],\n) -> np.ndarray:\n    \"\"\"Crop 3D volume using coordinates.\n\n    Args:\n        volume: Input volume with shape (z, y, x) or (z, y, x, channels)\n        crop_coords: Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping\n\n    Returns:\n        Cropped volume with same number of dimensions as input\n    \"\"\"\n    z_min, z_max, y_min, y_max, x_min, x_max = crop_coords\n\n    return volume[z_min:z_max, y_min:y_max, x_min:x_max]\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.cutout3d","title":"def cutout3d (volume, holes, fill_value) [view source on GitHub]","text":"

Cut out holes in 3D volume and fill them with a given value.

Source code in albumentations/augmentations/transforms3d/functional.py Python
def cutout3d(volume: np.ndarray, holes: np.ndarray, fill_value: ColorType) -> np.ndarray:\n    \"\"\"Cut out holes in 3D volume and fill them with a given value.\"\"\"\n    volume = volume.copy()\n    for z1, y1, x1, z2, y2, x2 in holes:\n        volume[z1:z2, y1:y2, x1:x2] = fill_value\n    return volume\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.pad_3d_with_params","title":"def pad_3d_with_params (volume, padding, value) [view source on GitHub]","text":"

Pad 3D image with given parameters.

Parameters:

Name Type Description volume ndarray

Input volume with shape (depth, height, width) or (depth, height, width, channels)

padding tuple[int, int, int, int, int, int]

Padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

value Union[float, collections.abc.Sequence[float]]

Padding value

Returns:

Type Description ndarray

Padded image with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py Python
def pad_3d_with_params(\n    volume: np.ndarray,\n    padding: tuple[int, int, int, int, int, int],  # (d_front, d_back, h_top, h_bottom, w_left, w_right)\n    value: ColorType,\n) -> np.ndarray:\n    \"\"\"Pad 3D image with given parameters.\n\n    Args:\n        volume: Input volume with shape (depth, height, width) or (depth, height, width, channels)\n        padding: Padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)\n        value: Padding value\n\n    Returns:\n        Padded image with same number of dimensions as input\n    \"\"\"\n    d_front, d_back, h_top, h_bottom, w_left, w_right = padding\n\n    # Skip if no padding is needed\n    if d_front == d_back == h_top == h_bottom == w_left == w_right == 0:\n        return volume\n\n    # Handle both 3D and 4D arrays\n    pad_width = [\n        (d_front, d_back),  # depth padding\n        (h_top, h_bottom),  # height padding\n        (w_left, w_right),  # width padding\n    ]\n\n    # Add channel padding if 4D array\n    if volume.ndim == NUM_VOLUME_DIMENSIONS:\n        pad_width.append((0, 0))  # no padding for channels\n\n    return np.pad(\n        volume,\n        pad_width=pad_width,\n        mode=\"constant\",\n        constant_values=value,\n    )\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.transform_cube","title":"def transform_cube (cube, index) [view source on GitHub]","text":"

Transform cube by index (0-47)

Parameters:

Name Type Description cube ndarray

Input array with shape (D, H, W) or (D, H, W, C)

index int

Integer from 0 to 47 specifying which transformation to apply

Returns:

Type Description ndarray

Transformed cube with same shape as input

Source code in albumentations/augmentations/transforms3d/functional.py Python
def transform_cube(cube: np.ndarray, index: int) -> np.ndarray:\n    \"\"\"Transform cube by index (0-47)\n\n    Args:\n        cube: Input array with shape (D, H, W) or (D, H, W, C)\n        index: Integer from 0 to 47 specifying which transformation to apply\n    Returns:\n        Transformed cube with same shape as input\n    \"\"\"\n    if not (0 <= index < 48):\n        raise ValueError(\"Index must be between 0 and 47\")\n\n    # First determine if we need reflection (indices 24-47)\n    needs_reflection = index >= 24\n    working_cube = cube[:, :, ::-1].copy() if needs_reflection else cube.copy()\n    rotation_index = index % 24\n\n    # Map rotation_index (0-23) to specific rotations\n    if rotation_index < 4:\n        # First 4: rotate around axis 0\n        return np.rot90(working_cube, rotation_index, axes=(1, 2))\n\n    if rotation_index < 8:\n        # Next 4: flip 180\u00b0 about axis 1, then rotate around axis 0\n        temp = np.rot90(working_cube, 2, axes=(0, 2))\n        return np.rot90(temp, rotation_index - 4, axes=(1, 2))\n\n    if rotation_index < 16:\n        # Next 8: split between 90\u00b0 and 270\u00b0 about axis 1, then rotate around axis 2\n        if rotation_index < 12:\n            temp = np.rot90(working_cube, axes=(0, 2))\n            return np.rot90(temp, rotation_index - 8, axes=(0, 1))\n        temp = np.rot90(working_cube, -1, axes=(0, 2))\n        return np.rot90(temp, rotation_index - 12, axes=(0, 1))\n\n    # Final 8: split between rotations about axis 2, then rotate around axis 1\n    if rotation_index < 20:\n        temp = np.rot90(working_cube, axes=(0, 1))\n        return np.rot90(temp, rotation_index - 16, axes=(0, 2))\n    temp = np.rot90(working_cube, -1, axes=(0, 1))\n    return np.rot90(temp, rotation_index - 20, axes=(0, 2))\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/","title":"3D (Volumetric) transforms (augmentations.transforms3d.transforms)","text":""},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.BaseCropAndPad3D","title":"class BaseCropAndPad3D (pad_if_needed, fill, fill_mask, pad_position, p=1.0, always_apply=None) [view source on GitHub]","text":"

Base class for 3D transforms that need both cropping and padding.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class BaseCropAndPad3D(Transform3D):\n    \"\"\"Base class for 3D transforms that need both cropping and padding.\"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D)\n\n    class InitSchema(Transform3D.InitSchema):\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n        pad_position: Literal[\"center\", \"random\"]\n\n    def __init__(\n        self,\n        pad_if_needed: bool,\n        fill: ColorType,\n        fill_mask: ColorType,\n        pad_position: Literal[\"center\", \"random\"],\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.pad_if_needed = pad_if_needed\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.pad_position = pad_position\n\n    def _random_pad(self, pad: int) -> tuple[int, int]:\n        \"\"\"Helper function to calculate random padding for one dimension.\"\"\"\n        if pad > 0:\n            pad_start = self.py_random.randint(0, pad)\n            pad_end = pad - pad_start\n        else:\n            pad_start = pad_end = 0\n        return pad_start, pad_end\n\n    def _center_pad(self, pad: int) -> tuple[int, int]:\n        \"\"\"Helper function to calculate center padding for one dimension.\"\"\"\n        pad_start = pad // 2\n        pad_end = pad - pad_start\n        return pad_start, pad_end\n\n    def _get_pad_params(\n        self,\n        image_shape: tuple[int, int, int],\n        target_shape: tuple[int, int, int],\n    ) -> dict[str, Any] | None:\n        \"\"\"Calculate padding parameters if needed for 3D volumes.\"\"\"\n        if not self.pad_if_needed:\n            return None\n\n        z, h, w = image_shape\n        target_z, target_h, target_w = target_shape\n\n        # Calculate total padding needed for each dimension\n        z_pad = max(0, target_z - z)\n        h_pad = max(0, target_h - h)\n        w_pad = max(0, target_w - w)\n\n        if z_pad == 0 and h_pad == 0 and w_pad == 0:\n            return None\n\n        # For center padding, split equally\n        if self.pad_position == \"center\":\n            z_front, z_back = self._center_pad(z_pad)\n            h_top, h_bottom = self._center_pad(h_pad)\n            w_left, w_right = self._center_pad(w_pad)\n        # For random padding, randomly distribute the padding\n        else:  # random\n            z_front, z_back = self._random_pad(z_pad)\n            h_top, h_bottom = self._random_pad(h_pad)\n            w_left, w_right = self._random_pad(w_pad)\n\n        return {\n            \"pad_front\": z_front,\n            \"pad_back\": z_back,\n            \"pad_top\": h_top,\n            \"pad_bottom\": h_bottom,\n            \"pad_left\": w_left,\n            \"pad_right\": w_right,\n        }\n\n    def apply_to_volume(\n        self,\n        volume: np.ndarray,\n        crop_coords: tuple[int, int, int, int, int, int],\n        pad_params: dict[str, int] | None,\n        **params: Any,\n    ) -> np.ndarray:\n        # First crop\n        cropped = f3d.crop3d(volume, crop_coords)\n\n        # Then pad if needed\n        if pad_params is not None:\n            padding = (\n                pad_params[\"pad_front\"],\n                pad_params[\"pad_back\"],\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n            )\n            return f3d.pad_3d_with_params(\n                cropped,\n                padding=padding,\n                value=cast(ColorType, self.fill),\n            )\n\n        return cropped\n\n    def apply_to_mask3d(\n        self,\n        mask3d: np.ndarray,\n        crop_coords: tuple[int, int, int, int, int, int],\n        pad_params: dict[str, int] | None,\n        **params: Any,\n    ) -> np.ndarray:\n        # First crop\n        cropped = f3d.crop3d(mask3d, crop_coords)\n\n        # Then pad if needed\n        if pad_params is not None:\n            padding = (\n                pad_params[\"pad_front\"],\n                pad_params[\"pad_back\"],\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n            )\n            return f3d.pad_3d_with_params(\n                cropped,\n                padding=padding,\n                value=cast(ColorType, self.fill_mask),\n            )\n\n        return cropped\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.BasePad3D","title":"class BasePad3D (fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Base class for 3D padding transforms.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class BasePad3D(Transform3D):\n    \"\"\"Base class for 3D padding transforms.\"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D)\n\n    class InitSchema(Transform3D.InitSchema):\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def apply_to_volume(\n        self,\n        volume: np.ndarray,\n        padding: tuple[int, int, int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        if padding == (0, 0, 0, 0, 0, 0):\n            return volume\n        return f3d.pad_3d_with_params(\n            volume=volume,\n            padding=padding,\n            value=cast(ColorType, self.fill),\n        )\n\n    def apply_to_mask3d(\n        self,\n        mask3d: np.ndarray,\n        padding: tuple[int, int, int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        if padding == (0, 0, 0, 0, 0, 0):\n            return mask3d\n        return f3d.pad_3d_with_params(\n            volume=mask3d,\n            padding=padding,\n            value=cast(ColorType, self.fill_mask),\n        )\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CenterCrop3D","title":"class CenterCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop the center of 3D volume.

Parameters:

Name Type Description size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

If you want to perform cropping only in the XY plane while preserving all slices along the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CenterCrop3D(BaseCropAndPad3D):\n    \"\"\"Crop the center of 3D volume.\n\n    Args:\n        size (tuple[int, int, int]): Desired output size of the crop in format (depth, height, width)\n        pad_if_needed (bool): Whether to pad if the volume is smaller than desired crop size. Default: False\n        fill (ColorType): Padding value for image if pad_if_needed is True. Default: 0\n        fill_mask (ColorType): Padding value for mask if pad_if_needed is True. Default: 0\n        p (float): probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If you want to perform cropping only in the XY plane while preserving all slices along\n        the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop\n        to each slice independently, maintaining the full depth of the volume.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        size: Annotated[tuple[int, int, int], AfterValidator(check_range_bounds(1, None))]\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        size: tuple[int, int, int],\n        pad_if_needed: bool = False,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=\"center\",  # Center crop always uses center padding\n            p=p,\n        )\n        self.size = size\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        volume = data[\"volume\"]\n        z, h, w = volume.shape[:3]\n        target_z, target_h, target_w = self.size\n\n        # Get padding params if needed\n        pad_params = self._get_pad_params(\n            image_shape=(z, h, w),\n            target_shape=self.size,\n        )\n\n        # Update dimensions if padding is applied\n        if pad_params is not None:\n            z = z + pad_params[\"pad_front\"] + pad_params[\"pad_back\"]\n            h = h + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            w = w + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n        # Validate dimensions after padding\n        if z < target_z or h < target_h or w < target_w:\n            msg = (\n                f\"Crop size {self.size} is larger than padded image size ({z}, {h}, {w}). \"\n                f\"This should not happen - please report this as a bug.\"\n            )\n            raise ValueError(msg)\n\n        # For CenterCrop3D:\n        z_start = (z - target_z) // 2\n        h_start = (h - target_h) // 2\n        w_start = (w - target_w) // 2\n\n        crop_coords = (\n            z_start,\n            z_start + target_z,\n            h_start,\n            h_start + target_h,\n            w_start,\n            w_start + target_w,\n        )\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"pad_if_needed\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CoarseDropout3D","title":"class CoarseDropout3D (num_holes_range=(1, 1), hole_depth_range=(0.1, 0.2), hole_height_range=(0.1, 0.2), hole_width_range=(0.1, 0.2), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally, the corresponding regions in an associated 3D mask, to simulate occlusion and varied object sizes found in real-world volumetric data.

Parameters:

Name Type Description num_holes_range tuple[int, int]

Range (min, max) for the number of cuboid regions to drop out. Default: (1, 1)

hole_depth_range tuple[float, float]

Range (min, max) for the depth of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)

hole_height_range tuple[float, float]

Range (min, max) for the height of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)

hole_width_range tuple[float, float]

Range (min, max) for the width of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)

fill ColorType

Value for the dropped voxels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the 3D mask. If None, mask regions corresponding to volume dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

volume, mask3d

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges.
  • All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.
  • If you want to apply dropout only in the XY plane while preserving the full depth dimension, consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout to each slice independently, effectively creating cylindrical dropout regions that extend through the entire depth of the volume.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n>>> aug = A.CoarseDropout3D(\n...     num_holes_range=(3, 6),\n...     hole_depth_range=(0.1, 0.2),\n...     hole_height_range=(0.1, 0.2),\n...     hole_width_range=(0.1, 0.2),\n...     fill=0,\n...     p=1.0\n... )\n>>> transformed = aug(volume=volume, mask3d=mask3d)\n>>> transformed_volume, transformed_mask3d = transformed[\"volume\"], transformed[\"mask3d\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CoarseDropout3D(Transform3D):\n    \"\"\"CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally,\n    the corresponding regions in an associated 3D mask, to simulate occlusion and\n    varied object sizes found in real-world volumetric data.\n\n    Args:\n        num_holes_range (tuple[int, int]): Range (min, max) for the number of cuboid\n            regions to drop out. Default: (1, 1)\n        hole_depth_range (tuple[float, float]): Range (min, max) for the depth\n            of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)\n        hole_height_range (tuple[float, float]): Range (min, max) for the height\n            of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)\n        hole_width_range (tuple[float, float]): Range (min, max) for the width\n            of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)\n        fill (ColorType): Value for the dropped voxels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            Default: 0\n        fill_mask (ColorType | None): Fill value for dropout regions in the 3D mask.\n            If None, mask regions corresponding to volume dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual number and size of dropout regions are randomly chosen within the specified ranges.\n        - All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.\n        - If you want to apply dropout only in the XY plane while preserving the full depth dimension,\n          consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout\n          to each slice independently, effectively creating cylindrical dropout regions that extend\n          through the entire depth of the volume.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n        >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n        >>> aug = A.CoarseDropout3D(\n        ...     num_holes_range=(3, 6),\n        ...     hole_depth_range=(0.1, 0.2),\n        ...     hole_height_range=(0.1, 0.2),\n        ...     hole_width_range=(0.1, 0.2),\n        ...     fill=0,\n        ...     p=1.0\n        ... )\n        >>> transformed = aug(volume=volume, mask3d=mask3d)\n        >>> transformed_volume, transformed_mask3d = transformed[\"volume\"], transformed[\"mask3d\"]\n    \"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D)\n\n    class InitSchema(Transform3D.InitSchema):\n        num_holes_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_depth_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_height_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_width_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        fill: ColorType\n        fill_mask: ColorType | None\n\n        @staticmethod\n        def validate_range(range_value: tuple[float, float], range_name: str) -> None:\n            if not 0 <= range_value[0] <= range_value[1] <= 1:\n                raise ValueError(\n                    f\"All values in {range_name} should be in [0, 1] range and first value \"\n                    f\"should be less or equal than the second value. Got: {range_value}\",\n                )\n\n        @model_validator(mode=\"after\")\n        def check_ranges(self) -> Self:\n            self.validate_range(self.hole_depth_range, \"hole_depth_range\")\n            self.validate_range(self.hole_height_range, \"hole_height_range\")\n            self.validate_range(self.hole_width_range, \"hole_width_range\")\n            return self\n\n    def __init__(\n        self,\n        num_holes_range: tuple[int, int] = (1, 1),\n        hole_depth_range: tuple[float, float] = (0.1, 0.2),\n        hole_height_range: tuple[float, float] = (0.1, 0.2),\n        hole_width_range: tuple[float, float] = (0.1, 0.2),\n        fill: ColorType = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_holes_range = num_holes_range\n        self.hole_depth_range = hole_depth_range\n        self.hole_height_range = hole_height_range\n        self.hole_width_range = hole_width_range\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def calculate_hole_dimensions(\n        self,\n        volume_shape: tuple[int, int, int],\n        depth_range: tuple[float, float],\n        height_range: tuple[float, float],\n        width_range: tuple[float, float],\n        size: int,\n    ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:\n        \"\"\"Calculate random hole dimensions based on the provided ranges.\"\"\"\n        depth, height, width = volume_shape[:3]\n\n        hole_depths = np.maximum(1, np.ceil(depth * self.random_generator.uniform(*depth_range, size=size))).astype(int)\n        hole_heights = np.maximum(1, np.ceil(height * self.random_generator.uniform(*height_range, size=size))).astype(\n            int,\n        )\n        hole_widths = np.maximum(1, np.ceil(width * self.random_generator.uniform(*width_range, size=size))).astype(int)\n\n        return hole_depths, hole_heights, hole_widths\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        volume_shape = data[\"volume\"].shape[:3]\n\n        num_holes = self.py_random.randint(*self.num_holes_range)\n\n        hole_depths, hole_heights, hole_widths = self.calculate_hole_dimensions(\n            volume_shape,\n            self.hole_depth_range,\n            self.hole_height_range,\n            self.hole_width_range,\n            size=num_holes,\n        )\n\n        depth, height, width = volume_shape[:3]\n\n        z_min = self.random_generator.integers(0, depth - hole_depths + 1, size=num_holes)\n        y_min = self.random_generator.integers(0, height - hole_heights + 1, size=num_holes)\n        x_min = self.random_generator.integers(0, width - hole_widths + 1, size=num_holes)\n        z_max = z_min + hole_depths\n        y_max = y_min + hole_heights\n        x_max = x_min + hole_widths\n\n        holes = np.stack([z_min, y_min, x_min, z_max, y_max, x_max], axis=-1)\n\n        return {\"holes\": holes}\n\n    def apply_to_volume(self, volume: np.ndarray, holes: np.ndarray, **params: Any) -> np.ndarray:\n        if holes.size == 0:\n            return volume\n\n        return f3d.cutout3d(volume, holes, cast(ColorType, self.fill))\n\n    def apply_to_mask(self, mask: np.ndarray, holes: np.ndarray, **params: Any) -> np.ndarray:\n        if self.fill_mask is None or holes.size == 0:\n            return mask\n\n        return f3d.cutout3d(mask, holes, cast(ColorType, self.fill_mask))\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"num_holes_range\",\n            \"hole_depth_range\",\n            \"hole_height_range\",\n            \"hole_width_range\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CubicSymmetry","title":"class CubicSymmetry (p=1.0, always_apply=None) [view source on GitHub]","text":"

Applies a random cubic symmetry transformation to a 3D volume.

This transform is a 3D extension of D4. While D4 handles the 8 symmetries of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube. Like D4, this transform does not create any interpolation artifacts as it only remaps voxels from one position to another without any interpolation.

The 48 transformations consist of: - 24 rotations (orientation-preserving): * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24) - 24 rotoreflections (orientation-reversing): * Reflection through a plane followed by any of the 24 rotations

For a cube, these transformations preserve: - All face centers (6) - All vertex positions (8) - All edge centers (12)

works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for data augmentation in 3D medical imaging, crystallography, and voxel-based 3D modeling where the object's orientation is arbitrary.
  • All transformations preserve the object's chirality (handedness) when using pure rotations (indices 0-23) and invert it when using rotoreflections (indices 24-47).

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n>>> transform = A.CubicSymmetry(p=1.0)\n>>> transformed = transform(volume=volume, mask3d=mask3d)\n>>> transformed_volume = transformed[\"volume\"]\n>>> transformed_mask3d = transformed[\"mask3d\"]\n

See Also: - D4: The 2D version that handles the 8 symmetries of a square

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CubicSymmetry(Transform3D):\n    \"\"\"Applies a random cubic symmetry transformation to a 3D volume.\n\n    This transform is a 3D extension of D4. While D4 handles the 8 symmetries\n    of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube.\n    Like D4, this transform does not create any interpolation artifacts as it only remaps voxels\n    from one position to another without any interpolation.\n\n    The 48 transformations consist of:\n    - 24 rotations (orientation-preserving):\n        * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24)\n    - 24 rotoreflections (orientation-reversing):\n        * Reflection through a plane followed by any of the 24 rotations\n\n    For a cube, these transformations preserve:\n    - All face centers (6)\n    - All vertex positions (8)\n    - All edge centers (12)\n\n    works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for data augmentation in 3D medical imaging,\n          crystallography, and voxel-based 3D modeling where the object's orientation\n          is arbitrary.\n        - All transformations preserve the object's chirality (handedness) when using\n          pure rotations (indices 0-23) and invert it when using rotoreflections\n          (indices 24-47).\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n        >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n        >>> transform = A.CubicSymmetry(p=1.0)\n        >>> transformed = transform(volume=volume, mask3d=mask3d)\n        >>> transformed_volume = transformed[\"volume\"]\n        >>> transformed_mask3d = transformed[\"mask3d\"]\n\n    See Also:\n        - D4: The 2D version that handles the 8 symmetries of a square\n    \"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D)\n\n    def __init__(\n        self,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        # Randomly select one of 48 possible transformations\n        return {\"index\": self.py_random.randint(0, 47)}\n\n    def apply_to_volume(self, volume: np.ndarray, index: int, **params: Any) -> np.ndarray:\n        return f3d.transform_cube(volume, index)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.Pad3D","title":"class Pad3D (padding, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of a 3D volume by specified number of voxels.

Parameters:

Name Type Description padding int, tuple[int, int, int] or tuple[int, int, int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int, int] - symmetric padding (pad_z, pad_y, pad_x) where: - pad_z: padding for depth/z-axis (front/back) - pad_y: padding for height/y-axis (top/bottom) - pad_x: padding for width/x-axis (left/right) * tuple[int, int, int, int, int, int] - explicit padding per side in order: (front, top, left, back, bottom, right) where: - front/back: padding along z-axis (depth) - top/bottom: padding along y-axis (height) - left/right: padding along x-axis (width)

fill ColorType

Padding value for image

fill_mask ColorType

Padding value for mask

p float

probability of applying the transform. Default: 1.0.

Targets

volume, mask3d

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class Pad3D(BasePad3D):\n    \"\"\"Pad the sides of a 3D volume by specified number of voxels.\n\n    Args:\n        padding (int, tuple[int, int, int] or tuple[int, int, int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int, int] - symmetric padding (pad_z, pad_y, pad_x) where:\n                - pad_z: padding for depth/z-axis (front/back)\n                - pad_y: padding for height/y-axis (top/bottom)\n                - pad_x: padding for width/x-axis (left/right)\n            * tuple[int, int, int, int, int, int] - explicit padding per side in order:\n                (front, top, left, back, bottom, right) where:\n                - front/back: padding along z-axis (depth)\n                - top/bottom: padding along y-axis (height)\n                - left/right: padding along x-axis (width)\n        fill (ColorType): Padding value for image\n        fill_mask (ColorType): Padding value for mask\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),\n        with optional channel dimension as the last axis.\n    \"\"\"\n\n    class InitSchema(BasePad3D.InitSchema):\n        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int]\n\n        @field_validator(\"padding\")\n        @classmethod\n        def validate_padding(\n            cls,\n            v: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],\n        ) -> int | tuple[int, int, int] | tuple[int, int, int, int, int, int]:\n            if isinstance(v, int) and v < 0:\n                raise ValueError(\"Padding value must be non-negative\")\n            if isinstance(v, tuple) and not all(isinstance(i, int) and i >= 0 for i in v):\n                raise ValueError(\"Padding tuple must contain non-negative integers\")\n\n            return v\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        if isinstance(self.padding, int):\n            pad_d = pad_h = pad_w = self.padding\n            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)\n        elif len(self.padding) == NUM_DIMENSIONS:\n            pad_d, pad_h, pad_w = self.padding  # type: ignore[misc]\n            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)\n        else:\n            padding = self.padding  # type: ignore[assignment]\n\n        return {\"padding\": padding}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"padding\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.PadIfNeeded3D","title":"class PadIfNeeded3D (min_zyx=None, pad_divisor_zyx=None, position='center', fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions. If the pad_divisor_zyx is specified, the function additionally ensures that the volume dimensions are divisible by these values.

Parameters:

Name Type Description min_zyx tuple[int, int, int] | None

Minimum desired size as (depth, height, width). Ensures volume dimensions are at least these values. If not specified, pad_divisor_zyx must be provided.

pad_divisor_zyx tuple[int, int, int] | None

If set, pads each dimension to make it divisible by corresponding value in format (depth_div, height_div, width_div). If not specified, min_zyx must be provided.

position Literal[\"center\", \"random\"]

Position where the volume is to be placed after padding. Default is 'center'.

fill ColorType

Value to fill the border voxels for volume. Default: 0

fill_mask ColorType

Value to fill the border voxels for masks. Default: 0

p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class PadIfNeeded3D(BasePad3D):\n    \"\"\"Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions.\n    If the pad_divisor_zyx is specified, the function additionally ensures that the volume\n    dimensions are divisible by these values.\n\n    Args:\n        min_zyx (tuple[int, int, int] | None): Minimum desired size as (depth, height, width).\n            Ensures volume dimensions are at least these values.\n            If not specified, pad_divisor_zyx must be provided.\n        pad_divisor_zyx (tuple[int, int, int] | None): If set, pads each dimension to make it\n            divisible by corresponding value in format (depth_div, height_div, width_div).\n            If not specified, min_zyx must be provided.\n        position (Literal[\"center\", \"random\"]): Position where the volume is to be placed after padding.\n            Default is 'center'.\n        fill (ColorType): Value to fill the border voxels for volume. Default: 0\n        fill_mask (ColorType): Value to fill the border voxels for masks. Default: 0\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),\n        with optional channel dimension as the last axis.\n    \"\"\"\n\n    class InitSchema(BasePad3D.InitSchema):\n        min_zyx: Annotated[tuple[int, int, int] | None, AfterValidator(check_range_bounds(0, None))]\n        pad_divisor_zyx: Annotated[tuple[int, int, int] | None, AfterValidator(check_range_bounds(1, None))]\n        position: Literal[\"center\", \"random\"]\n\n        @model_validator(mode=\"after\")\n        def validate_params(self) -> Self:\n            if self.min_zyx is None and self.pad_divisor_zyx is None:\n                msg = \"At least one of min_zyx or pad_divisor_zyx must be set\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        min_zyx: tuple[int, int, int] | None = None,\n        pad_divisor_zyx: tuple[int, int, int] | None = None,\n        position: Literal[\"center\", \"random\"] = \"center\",\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.min_zyx = min_zyx\n        self.pad_divisor_zyx = pad_divisor_zyx\n        self.position = position\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        depth, height, width = data[\"volume\"].shape[:3]\n        sizes = (depth, height, width)\n\n        paddings = [\n            fgeometric.get_dimension_padding(\n                current_size=size,\n                min_size=self.min_zyx[i] if self.min_zyx else None,\n                divisor=self.pad_divisor_zyx[i] if self.pad_divisor_zyx else None,\n            )\n            for i, size in enumerate(sizes)\n        ]\n\n        padding = f3d.adjust_padding_by_position3d(\n            paddings=paddings,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\"padding\": padding}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_zyx\",\n            \"pad_divisor_zyx\",\n            \"position\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.RandomCrop3D","title":"class RandomCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop random part of 3D volume.

Parameters:

Name Type Description size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d

Image types: uint8, float32

Note

If you want to perform random cropping only in the XY plane while preserving all slices along the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class RandomCrop3D(BaseCropAndPad3D):\n    \"\"\"Crop random part of 3D volume.\n\n    Args:\n        size (tuple[int, int, int]): Desired output size of the crop in format (depth, height, width)\n        pad_if_needed (bool): Whether to pad if the volume is smaller than desired crop size. Default: False\n        fill (ColorType): Padding value for image if pad_if_needed is True. Default: 0\n        fill_mask (ColorType): Padding value for mask if pad_if_needed is True. Default: 0\n        p (float): probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If you want to perform random cropping only in the XY plane while preserving all slices along\n        the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop\n        to each slice independently, maintaining the full depth of the volume.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        size: Annotated[tuple[int, int, int], AfterValidator(check_range_bounds(1, None))]\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        size: tuple[int, int, int],\n        pad_if_needed: bool = False,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=\"random\",  # Random crop uses random padding position\n            p=p,\n        )\n        self.size = size\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        volume = data[\"volume\"]\n        z, h, w = volume.shape[:3]\n        target_z, target_h, target_w = self.size\n\n        # Get padding params if needed\n        pad_params = self._get_pad_params(\n            image_shape=(z, h, w),\n            target_shape=self.size,\n        )\n\n        # Update dimensions if padding is applied\n        if pad_params is not None:\n            z = z + pad_params[\"pad_front\"] + pad_params[\"pad_back\"]\n            h = h + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            w = w + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n        # Calculate random crop coordinates\n        z_start = self.py_random.randint(0, max(0, z - target_z))\n        h_start = self.py_random.randint(0, max(0, h - target_h))\n        w_start = self.py_random.randint(0, max(0, w - target_w))\n\n        crop_coords = (\n            z_start,\n            z_start + target_z,\n            h_start,\n            h_start + target_h,\n            w_start,\n            w_start + target_w,\n        )\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"pad_if_needed\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/core/","title":"Index","text":"
  • Composition API (albumentations.core.composition)
  • Serialization API (albumentations.core.serialization)
  • Transforms Interface (albumentations.core.transforms_interface)
  • Helper functions for working with bounding boxes (albumentations.core.bbox_utils)
  • Helper functions for working with keypoints (albumentations.core.keypoints_utils)
"},{"location":"api_reference/core/bbox_utils/","title":"Helper functions for working with bounding boxes (augmentations.core.bbox_utils)","text":""},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.BboxParams","title":"class BboxParams (format, label_fields=None, min_area=0.0, min_visibility=0.0, min_width=0.0, min_height=0.0, check_each_transform=True, clip=False) [view source on GitHub]","text":"

Parameters of bounding boxes

Parameters:

Name Type Description format Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"]

format of bounding boxes.

The coco format [x_min, y_min, width, height], e.g. [97, 12, 150, 200]. The pascal_voc format [x_min, y_min, x_max, y_max], e.g. [97, 12, 247, 212]. The albumentations format is like pascal_voc, but normalized, in other words: [x_min, y_min, x_max, y_max], e.g. [0.2, 0.3, 0.4, 0.5]. The yolo format [x, y, width, height], e.g. [0.1, 0.2, 0.3, 0.4]; x, y - normalized bbox center; width, height - normalized bbox width and height.

label_fields list

List of fields joined with boxes, e.g., labels.

min_area float

Minimum area of a bounding box in pixels or normalized units. Bounding boxes with an area less than this value will be removed. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain in the list. Bounding boxes with a visible area less than this fraction will be removed. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels or normalized units. Bounding boxes with a width less than this value will be removed. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels or normalized units. Bounding boxes with a height less than this value will be removed. Default: 0.0.

check_each_transform bool

If True, bounding boxes will be checked after each dual transform. Default: True.

clip bool

If True, bounding boxes will be clipped to the image borders before applying any transform. Default: False.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/bbox_utils.py Python
class BboxParams(Params):\n    \"\"\"Parameters of bounding boxes\n\n    Args:\n        format Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"]: format of bounding boxes.\n\n            The `coco` format\n                `[x_min, y_min, width, height]`, e.g. [97, 12, 150, 200].\n            The `pascal_voc` format\n                `[x_min, y_min, x_max, y_max]`, e.g. [97, 12, 247, 212].\n            The `albumentations` format\n                is like `pascal_voc`, but normalized,\n                in other words: `[x_min, y_min, x_max, y_max]`, e.g. [0.2, 0.3, 0.4, 0.5].\n            The `yolo` format\n                `[x, y, width, height]`, e.g. [0.1, 0.2, 0.3, 0.4];\n                `x`, `y` - normalized bbox center; `width`, `height` - normalized bbox width and height.\n\n        label_fields (list): List of fields joined with boxes, e.g., labels.\n        min_area (float): Minimum area of a bounding box in pixels or normalized units.\n            Bounding boxes with an area less than this value will be removed. Default: 0.0.\n        min_visibility (float): Minimum fraction of area for a bounding box to remain in the list.\n            Bounding boxes with a visible area less than this fraction will be removed. Default: 0.0.\n        min_width (float): Minimum width of a bounding box in pixels or normalized units.\n            Bounding boxes with a width less than this value will be removed. Default: 0.0.\n        min_height (float): Minimum height of a bounding box in pixels or normalized units.\n            Bounding boxes with a height less than this value will be removed. Default: 0.0.\n        check_each_transform (bool): If True, bounding boxes will be checked after each dual transform. Default: True.\n        clip (bool): If True, bounding boxes will be clipped to the image borders before applying any transform.\n            Default: False.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        format: Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"],  # noqa: A002\n        label_fields: Sequence[Any] | None = None,\n        min_area: float = 0.0,\n        min_visibility: float = 0.0,\n        min_width: float = 0.0,\n        min_height: float = 0.0,\n        check_each_transform: bool = True,\n        clip: bool = False,\n    ):\n        super().__init__(format, label_fields)\n        self.min_area = min_area\n        self.min_visibility = min_visibility\n        self.min_width = min_width\n        self.min_height = min_height\n        self.check_each_transform = check_each_transform\n        self.clip = clip\n\n    def to_dict_private(self) -> dict[str, Any]:\n        data = super().to_dict_private()\n        data.update(\n            {\n                \"min_area\": self.min_area,\n                \"min_visibility\": self.min_visibility,\n                \"min_width\": self.min_width,\n                \"min_height\": self.min_height,\n                \"check_each_transform\": self.check_each_transform,\n                \"clip\": self.clip,\n            },\n        )\n        return data\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return \"BboxParams\"\n\n    def __repr__(self) -> str:\n        return (\n            f\"BboxParams(format={self.format}, label_fields={self.label_fields}, min_area={self.min_area},\"\n            f\" min_visibility={self.min_visibility}, min_width={self.min_width}, min_height={self.min_height},\"\n            f\" check_each_transform={self.check_each_transform}, clip={self.clip})\"\n        )\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.bboxes_from_masks","title":"def bboxes_from_masks (masks) [view source on GitHub]","text":"

Create bounding boxes from binary masks (fast version)

Parameters:

Name Type Description masks np.ndarray

Binary masks of shape (H, W) or (N, H, W) where N is the number of masks, and H, W are the height and width of each mask.

Returns:

Type Description np.ndarray

An array of bounding boxes with shape (N, 4), where each row is (x_min, y_min, x_max, y_max).

Source code in albumentations/core/bbox_utils.py Python
def bboxes_from_masks(masks: np.ndarray) -> np.ndarray:\n    \"\"\"Create bounding boxes from binary masks (fast version)\n\n    Args:\n        masks (np.ndarray): Binary masks of shape (H, W) or (N, H, W) where N is the number of masks,\n                           and H, W are the height and width of each mask.\n\n    Returns:\n        np.ndarray: An array of bounding boxes with shape (N, 4), where each row is\n                   (x_min, y_min, x_max, y_max).\n    \"\"\"\n    # Handle single mask case by adding batch dimension\n    if len(masks.shape) == MONO_CHANNEL_DIMENSIONS:\n        masks = masks[np.newaxis, ...]\n\n    rows = np.any(masks, axis=2)\n    cols = np.any(masks, axis=1)\n\n    bboxes = np.zeros((masks.shape[0], 4), dtype=np.int32)\n\n    for i, (row, col) in enumerate(zip(rows, cols)):\n        if not np.any(row) or not np.any(col):\n            bboxes[i] = [-1, -1, -1, -1]\n        else:\n            y_min, y_max = np.where(row)[0][[0, -1]]\n            x_min, x_max = np.where(col)[0][[0, -1]]\n            bboxes[i] = [x_min, y_min, x_max + 1, y_max + 1]\n\n    return bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.calculate_bbox_areas_in_pixels","title":"def calculate_bbox_areas_in_pixels (bboxes, image_shape) [view source on GitHub]","text":"

Calculate areas for multiple bounding boxes.

This function computes the areas of bounding boxes given their normalized coordinates and the dimensions of the image they belong to. The bounding boxes are expected to be in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of shape (N, 4+) where N is the number of bounding boxes. Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates. Additional columns beyond the first 4 are ignored.

image_shape tuple[int, int]

A tuple containing the height and width of the image (height, width).

Returns:

Type Description np.ndarray

A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels. Returns an empty array if the input bboxes is empty.

Note

  • The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min). Invalid bounding boxes may result in negative areas.
  • The function preserves the input array and creates a copy for internal calculations.
  • The returned areas are in pixel units, not normalized.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])\n>>> image_shape = (100, 100)\n>>> areas = calculate_bbox_areas(bboxes, image_shape)\n>>> print(areas)\n[1600. 3600.]\n
Source code in albumentations/core/bbox_utils.py Python
def calculate_bbox_areas_in_pixels(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:\n    \"\"\"Calculate areas for multiple bounding boxes.\n\n    This function computes the areas of bounding boxes given their normalized coordinates\n    and the dimensions of the image they belong to. The bounding boxes are expected to be\n    in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).\n\n    Args:\n        bboxes (np.ndarray): A numpy array of shape (N, 4+) where N is the number of bounding boxes.\n                             Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates.\n                             Additional columns beyond the first 4 are ignored.\n        image_shape (tuple[int, int]): A tuple containing the height and width of the image (height, width).\n\n    Returns:\n        np.ndarray: A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels.\n                    Returns an empty array if the input `bboxes` is empty.\n\n    Note:\n        - The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min).\n          Invalid bounding boxes may result in negative areas.\n        - The function preserves the input array and creates a copy for internal calculations.\n        - The returned areas are in pixel units, not normalized.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])\n        >>> image_shape = (100, 100)\n        >>> areas = calculate_bbox_areas(bboxes, image_shape)\n        >>> print(areas)\n        [1600. 3600.]\n    \"\"\"\n    if len(bboxes) == 0:\n        return np.array([], dtype=np.float32)\n\n    height, width = image_shape\n    bboxes_denorm = bboxes.copy()\n    bboxes_denorm[:, [0, 2]] *= width\n    bboxes_denorm[:, [1, 3]] *= height\n    return (bboxes_denorm[:, 2] - bboxes_denorm[:, 0]) * (bboxes_denorm[:, 3] - bboxes_denorm[:, 1])\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.check_bboxes","title":"def check_bboxes (bboxes) [view source on GitHub]","text":"

Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.

Parameters:

Name Type Description bboxes np.ndarray

numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.

Exceptions:

Type Description ValueError

If any bbox is invalid.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef check_bboxes(bboxes: np.ndarray) -> None:\n    \"\"\"Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.\n\n    Args:\n        bboxes: numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.\n\n    Raises:\n        ValueError: If any bbox is invalid.\n    \"\"\"\n    # Check if all values are in range [0, 1]\n    in_range = (bboxes[:, :4] >= 0) & (bboxes[:, :4] <= 1)\n    close_to_zero = np.isclose(bboxes[:, :4], 0)\n    close_to_one = np.isclose(bboxes[:, :4], 1)\n    valid_range = in_range | close_to_zero | close_to_one\n\n    if not np.all(valid_range):\n        invalid_idx = np.where(~np.all(valid_range, axis=1))[0][0]\n        invalid_bbox = bboxes[invalid_idx]\n        invalid_coord = [\"x_min\", \"y_min\", \"x_max\", \"y_max\"][np.where(~valid_range[invalid_idx])[0][0]]\n        invalid_value = invalid_bbox[np.where(~valid_range[invalid_idx])[0][0]]\n        raise ValueError(\n            f\"Expected {invalid_coord} for bbox {invalid_bbox} to be in the range [0.0, 1.0], got {invalid_value}.\",\n        )\n\n    # Check if x_max > x_min and y_max > y_min\n    valid_order = (bboxes[:, 2] > bboxes[:, 0]) & (bboxes[:, 3] > bboxes[:, 1])\n\n    if not np.all(valid_order):\n        invalid_idx = np.where(~valid_order)[0][0]\n        invalid_bbox = bboxes[invalid_idx]\n        if invalid_bbox[2] <= invalid_bbox[0]:\n            raise ValueError(f\"x_max is less than or equal to x_min for bbox {invalid_bbox}.\")\n\n        raise ValueError(f\"y_max is less than or equal to y_min for bbox {invalid_bbox}.\")\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.clip_bboxes","title":"def clip_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Clips the bounding box coordinates to ensure they fit within the boundaries of an image.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (num_boxes, 4+) in normalized format. The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape Tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

The clipped bounding boxes, normalized to the image dimensions.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef clip_bboxes(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:\n    \"\"\"Clips the bounding box coordinates to ensure they fit within the boundaries of an image.\n\n    Parameters:\n        bboxes (np.ndarray): Array of bounding boxes with shape (num_boxes, 4+) in normalized format.\n                             The first 4 columns are [x_min, y_min, x_max, y_max].\n        image_shape (Tuple[int, int]): Image shape (height, width).\n\n    Returns:\n        np.ndarray: The clipped bounding boxes, normalized to the image dimensions.\n\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Denormalize bboxes\n    denorm_bboxes = denormalize_bboxes(bboxes, image_shape)\n\n    ## Note:\n    # It could be tempting to use cols - 1 and rows - 1 as the upper bounds for the clipping\n\n    # But this would cause the bounding box to be clipped to the image dimensions - 1 which is not what we want.\n    # Bounding box lives not in the middle of pixels but between them.\n\n    # Example: for image with height 100, width 100, the pixel values are in the range [0, 99]\n    # but if we want bounding box to be 1 pixel width and height and lie on the boundary of the image\n    # it will be described as [99, 99, 100, 100] => clip by image_size - 1 will lead to [99, 99, 99, 99]\n    # which is incorrect\n\n    # It could be also tempting to clip `x_min`` to `cols - 1`` and `y_min` to `rows - 1`, but this also leads\n    # to another error. If image fully lies outside of the visible area and min_area is set to 0, then\n    # the bounding box will be clipped to the image size - 1 and will be 1 pixel in size and fully visible,\n    # but it should be completely removed.\n\n    # Clip coordinates\n    denorm_bboxes[:, [0, 2]] = np.clip(denorm_bboxes[:, [0, 2]], 0, width, out=denorm_bboxes[:, [0, 2]])\n    denorm_bboxes[:, [1, 3]] = np.clip(denorm_bboxes[:, [1, 3]], 0, height, out=denorm_bboxes[:, [1, 3]])\n\n    # Normalize clipped bboxes\n    return normalize_bboxes(denorm_bboxes, image_shape)\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.convert_bboxes_from_albumentations","title":"def convert_bboxes_from_albumentations (bboxes, target_format, image_shape, check_validity=False) [view source on GitHub]","text":"

Convert bounding boxes from the format used by albumentations to a specified format.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

target_format Literal['coco', 'pascal_voc', 'yolo']

Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.

image_shape tuple[int, int]

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description np.ndarray

An array of bounding boxes in the target format with shape (num_bboxes, 4+).

Exceptions:

Type Description ValueError

If target_format is not 'coco', 'pascal_voc' or 'yolo'.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef convert_bboxes_from_albumentations(\n    bboxes: np.ndarray,\n    target_format: Literal[\"coco\", \"pascal_voc\", \"yolo\"],\n    image_shape: tuple[int, int],\n    check_validity: bool = False,\n) -> np.ndarray:\n    \"\"\"Convert bounding boxes from the format used by albumentations to a specified format.\n\n    Args:\n        bboxes: A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+).\n                The first 4 columns are [x_min, y_min, x_max, y_max].\n        target_format: Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.\n        image_shape: Image shape (height, width).\n        check_validity: Check if all boxes are valid boxes.\n\n    Returns:\n        np.ndarray: An array of bounding boxes in the target format with shape (num_bboxes, 4+).\n\n    Raises:\n        ValueError: If `target_format` is not 'coco', 'pascal_voc' or 'yolo'.\n    \"\"\"\n    if target_format not in {\"coco\", \"pascal_voc\", \"yolo\"}:\n        raise ValueError(\n            f\"Unknown target_format {target_format}. Supported formats are: 'coco', 'pascal_voc' and 'yolo'\",\n        )\n\n    if check_validity:\n        check_bboxes(bboxes)\n\n    converted_bboxes = np.zeros_like(bboxes)\n    converted_bboxes[:, 4:] = bboxes[:, 4:]  # Preserve additional columns\n\n    denormalized_bboxes = denormalize_bboxes(bboxes[:, :4], image_shape) if target_format != \"yolo\" else bboxes[:, :4]\n\n    if target_format == \"coco\":\n        converted_bboxes[:, 0] = denormalized_bboxes[:, 0]  # x_min\n        converted_bboxes[:, 1] = denormalized_bboxes[:, 1]  # y_min\n        converted_bboxes[:, 2] = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]  # width\n        converted_bboxes[:, 3] = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]  # height\n    elif target_format == \"yolo\":\n        converted_bboxes[:, 0] = (denormalized_bboxes[:, 0] + denormalized_bboxes[:, 2]) / 2  # x_center\n        converted_bboxes[:, 1] = (denormalized_bboxes[:, 1] + denormalized_bboxes[:, 3]) / 2  # y_center\n        converted_bboxes[:, 2] = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]  # width\n        converted_bboxes[:, 3] = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]  # height\n    else:  # pascal_voc\n        converted_bboxes[:, :4] = denormalized_bboxes\n\n    return converted_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.convert_bboxes_to_albumentations","title":"def convert_bboxes_to_albumentations (bboxes, source_format, image_shape, check_validity=False) [view source on GitHub]","text":"

Convert bounding boxes from a specified format to the format used by albumentations: normalized coordinates of top-left and bottom-right corners of the bounding box in the form of (x_min, y_min, x_max, y_max) e.g. (0.15, 0.27, 0.67, 0.5).

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+).

source_format Literal['coco', 'pascal_voc', 'yolo']

Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.

image_shape tuple[int, int]

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description np.ndarray

An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).

Exceptions:

Type Description ValueError

If source_format is not 'coco', 'pascal_voc', or 'yolo'.

ValueError

If in YOLO format, any coordinates are not in the range (0, 1].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef convert_bboxes_to_albumentations(\n    bboxes: np.ndarray,\n    source_format: Literal[\"coco\", \"pascal_voc\", \"yolo\"],\n    image_shape: tuple[int, int],\n    check_validity: bool = False,\n) -> np.ndarray:\n    \"\"\"Convert bounding boxes from a specified format to the format used by albumentations:\n    normalized coordinates of top-left and bottom-right corners of the bounding box in the form of\n    `(x_min, y_min, x_max, y_max)` e.g. `(0.15, 0.27, 0.67, 0.5)`.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n        source_format: Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.\n        image_shape: Image shape (height, width).\n        check_validity: Check if all boxes are valid boxes.\n\n    Returns:\n        np.ndarray: An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).\n\n    Raises:\n        ValueError: If `source_format` is not 'coco', 'pascal_voc', or 'yolo'.\n        ValueError: If in YOLO format, any coordinates are not in the range (0, 1].\n    \"\"\"\n    if source_format not in {\"coco\", \"pascal_voc\", \"yolo\"}:\n        raise ValueError(\n            f\"Unknown source_format {source_format}. Supported formats are: 'coco', 'pascal_voc' and 'yolo'\",\n        )\n\n    bboxes = bboxes.copy().astype(np.float32)\n    converted_bboxes = np.zeros_like(bboxes)\n    converted_bboxes[:, 4:] = bboxes[:, 4:]  # Preserve additional columns\n\n    if source_format == \"coco\":\n        converted_bboxes[:, 0] = bboxes[:, 0]  # x_min\n        converted_bboxes[:, 1] = bboxes[:, 1]  # y_min\n        converted_bboxes[:, 2] = bboxes[:, 0] + bboxes[:, 2]  # x_max\n        converted_bboxes[:, 3] = bboxes[:, 1] + bboxes[:, 3]  # y_max\n    elif source_format == \"yolo\":\n        if check_validity and np.any((bboxes[:, :4] <= 0) | (bboxes[:, :4] > 1)):\n            raise ValueError(f\"In YOLO format all coordinates must be float and in range (0, 1], got {bboxes}\")\n\n        w_half, h_half = bboxes[:, 2] / 2, bboxes[:, 3] / 2\n        converted_bboxes[:, 0] = bboxes[:, 0] - w_half  # x_min\n        converted_bboxes[:, 1] = bboxes[:, 1] - h_half  # y_min\n        converted_bboxes[:, 2] = bboxes[:, 0] + w_half  # x_max\n        converted_bboxes[:, 3] = bboxes[:, 1] + h_half  # y_max\n    else:  # pascal_voc\n        converted_bboxes[:, :4] = bboxes[:, :4]\n\n    if source_format != \"yolo\":\n        converted_bboxes[:, :4] = normalize_bboxes(converted_bboxes[:, :4], image_shape)\n\n    if check_validity:\n        check_bboxes(converted_bboxes)\n\n    return converted_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.denormalize_bboxes","title":"def denormalize_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Denormalize array of bounding boxes.

Parameters:

Name Type Description bboxes np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

image_shape tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef denormalize_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Denormalize  array of bounding boxes.\n\n    Args:\n        bboxes: Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n        image_shape: Image shape `(height, width)`.\n\n    Returns:\n        Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    denormalized = bboxes.copy().astype(float)\n    denormalized[:, [0, 2]] *= cols\n    denormalized[:, [1, 3]] *= rows\n    return denormalized\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.filter_bboxes","title":"def filter_bboxes (bboxes, image_shape, min_area=0.0, min_visibility=0.0, min_width=1.0, min_height=1.0) [view source on GitHub]","text":"

Remove bounding boxes that either lie outside of the visible area by more than min_visibility or whose area in pixels is under the threshold set by min_area. Also crops boxes to final image size.

Parameters:

Name Type Description bboxes np.ndarray

numpy array of bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Image shape (height, width).

min_area float

Minimum area of a bounding box in pixels. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels. Default: 0.0.

Returns:

Type Description np.ndarray

numpy array of filtered bounding boxes.

Source code in albumentations/core/bbox_utils.py Python
def filter_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n    min_area: float = 0.0,\n    min_visibility: float = 0.0,\n    min_width: float = 1.0,\n    min_height: float = 1.0,\n) -> np.ndarray:\n    \"\"\"Remove bounding boxes that either lie outside of the visible area by more than min_visibility\n    or whose area in pixels is under the threshold set by `min_area`. Also crops boxes to final image size.\n\n    Args:\n        bboxes: numpy array of bounding boxes with shape (num_bboxes, 4+).\n                The first 4 columns are [x_min, y_min, x_max, y_max].\n        image_shape: Image shape (height, width).\n        min_area: Minimum area of a bounding box in pixels. Default: 0.0.\n        min_visibility: Minimum fraction of area for a bounding box to remain. Default: 0.0.\n        min_width: Minimum width of a bounding box in pixels. Default: 0.0.\n        min_height: Minimum height of a bounding box in pixels. Default: 0.0.\n\n    Returns:\n        numpy array of filtered bounding boxes.\n    \"\"\"\n    epsilon = 1e-7\n\n    if len(bboxes) == 0:\n        return np.array([], dtype=np.float32).reshape(0, 4)\n\n    # Calculate areas of bounding boxes before clipping in pixels\n    denormalized_box_areas = calculate_bbox_areas_in_pixels(bboxes, image_shape)\n\n    # Clip bounding boxes in ratio\n    clipped_bboxes = clip_bboxes(bboxes, image_shape)\n\n    # Calculate areas of clipped bounding boxes in pixels\n    clipped_box_areas = calculate_bbox_areas_in_pixels(clipped_bboxes, image_shape)\n\n    # Calculate width and height of the clipped bounding boxes\n    denormalized_bboxes = denormalize_bboxes(clipped_bboxes[:, :4], image_shape)\n\n    clipped_widths = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]\n    clipped_heights = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]\n\n    # Create a mask for bboxes that meet all criteria\n    mask = (\n        (denormalized_box_areas >= epsilon)\n        & (clipped_box_areas >= min_area - epsilon)\n        & (clipped_box_areas / denormalized_box_areas >= min_visibility - epsilon)\n        & (clipped_widths >= min_width - epsilon)\n        & (clipped_heights >= min_height - epsilon)\n    )\n\n    # Apply the mask to get the filtered bboxes\n    filtered_bboxes = clipped_bboxes[mask]\n\n    return np.array([], dtype=np.float32).reshape(0, 4) if len(filtered_bboxes) == 0 else filtered_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.masks_from_bboxes","title":"def masks_from_bboxes (bboxes, img_shape) [view source on GitHub]","text":"

Create binary masks from multiple bounding boxes

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4), where N is the number of boxes

img_shape tuple[int, int]

Image shape (height, width)

Returns:

Type Description masks

Array of binary masks with shape (N, height, width)

Source code in albumentations/core/bbox_utils.py Python
def masks_from_bboxes(bboxes: np.ndarray, img_shape: tuple[int, int]) -> np.ndarray:\n    \"\"\"Create binary masks from multiple bounding boxes\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4), where N is the number of boxes\n        img_shape: Image shape (height, width)\n\n    Returns:\n        masks: Array of binary masks with shape (N, height, width)\n\n    \"\"\"\n    height, width = img_shape[:2]\n    masks = np.zeros((len(bboxes), height, width), dtype=np.uint8)\n    y, x = np.ogrid[:height, :width]\n\n    for i, (x_min, y_min, x_max, y_max) in enumerate(bboxes[:, :4].astype(int)):\n        masks[i] = (x_min <= x) & (x < x_max) & (y_min <= y) & (y < y_max)\n\n    return masks\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.normalize_bboxes","title":"def normalize_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Normalize array of bounding boxes.

Parameters:

Name Type Description bboxes np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

image_shape tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef normalize_bboxes(bboxes: np.ndarray, image_shape: tuple[int, int]) -> np.ndarray:\n    \"\"\"Normalize array of bounding boxes.\n\n    Args:\n        bboxes: Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n        image_shape: Image shape `(height, width)`.\n\n    Returns:\n        Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n\n    \"\"\"\n    rows, cols = image_shape[:2]\n    normalized = bboxes.copy().astype(float)\n    normalized[:, [0, 2]] /= cols\n    normalized[:, [1, 3]] /= rows\n    return normalized\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.union_of_bboxes","title":"def union_of_bboxes (bboxes, erosion_rate) [view source on GitHub]","text":"

Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.

Parameters:

Name Type Description bboxes np.ndarray

List of bounding boxes

erosion_rate float

How much each bounding box can be shrunk, useful for erosive cropping. Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.

Returns:

Type Description np.ndarray | None

A bounding box (x_min, y_min, x_max, y_max) or None if no bboxes are given or if the bounding boxes become invalid after erosion.

Source code in albumentations/core/bbox_utils.py Python
def union_of_bboxes(bboxes: np.ndarray, erosion_rate: float) -> np.ndarray | None:\n    \"\"\"Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.\n\n    Args:\n        bboxes (np.ndarray): List of bounding boxes\n        erosion_rate (float): How much each bounding box can be shrunk, useful for erosive cropping.\n            Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.\n\n    Returns:\n        np.ndarray | None: A bounding box `(x_min, y_min, x_max, y_max)` or None if no bboxes are given or if\n                    the bounding boxes become invalid after erosion.\n    \"\"\"\n    if not bboxes.size:\n        return None\n\n    if erosion_rate == 1:\n        return None\n\n    if bboxes.shape[0] == 1:\n        return bboxes[0][:4]\n\n    epsilon = 1e-6\n\n    x_min, y_min = np.min(bboxes[:, :2], axis=0)\n    x_max, y_max = np.max(bboxes[:, 2:4], axis=0)\n\n    width = x_max - x_min\n    height = y_max - y_min\n\n    erosion_x = width * erosion_rate * 0.5\n    erosion_y = height * erosion_rate * 0.5\n\n    x_min += erosion_x\n    y_min += erosion_y\n    x_max -= erosion_x\n    y_max -= erosion_y\n\n    if abs(x_max - x_min) < epsilon or abs(y_max - y_min) < epsilon:\n        return None\n\n    return np.array([x_min, y_min, x_max, y_max], dtype=np.float32)\n
"},{"location":"api_reference/core/composition/","title":"Composition API (core.composition)","text":""},{"location":"api_reference/core/composition/#albumentations.core.composition.BaseCompose","title":"class BaseCompose (transforms, p, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub]","text":"

Base class for composing multiple transforms together.

This class serves as a foundation for creating compositions of transforms in the Albumentations library. It provides basic functionality for managing a sequence of transforms and applying them to data.

Attributes:

Name Type Description transforms List[TransformType]

A list of transforms to be applied.

p float

Probability of applying the compose. Should be in the range [0, 1].

replay_mode bool

If True, the compose is in replay mode.

_additional_targets Dict[str, str]

Additional targets for transforms.

_available_keys Set[str]

Set of available keys for data.

processors Dict[str, Union[BboxProcessor, KeypointsProcessor]]

Processors for specific data types.

Parameters:

Name Type Description transforms TransformsSeqType

A sequence of transforms to compose.

p float

Probability of applying the compose.

Exceptions:

Type Description ValueError

If an invalid additional target is specified.

Note

  • Subclasses should implement the call method to define how the composition is applied to data.
  • The class supports serialization and deserialization of transforms.
  • It provides methods for adding targets, setting deterministic behavior, and checking data validity post-transform.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class BaseCompose(Serializable):\n    \"\"\"Base class for composing multiple transforms together.\n\n    This class serves as a foundation for creating compositions of transforms\n    in the Albumentations library. It provides basic functionality for\n    managing a sequence of transforms and applying them to data.\n\n    Attributes:\n        transforms (List[TransformType]): A list of transforms to be applied.\n        p (float): Probability of applying the compose. Should be in the range [0, 1].\n        replay_mode (bool): If True, the compose is in replay mode.\n        _additional_targets (Dict[str, str]): Additional targets for transforms.\n        _available_keys (Set[str]): Set of available keys for data.\n        processors (Dict[str, Union[BboxProcessor, KeypointsProcessor]]): Processors for specific data types.\n\n    Args:\n        transforms (TransformsSeqType): A sequence of transforms to compose.\n        p (float): Probability of applying the compose.\n\n    Raises:\n        ValueError: If an invalid additional target is specified.\n\n    Note:\n        - Subclasses should implement the __call__ method to define how\n          the composition is applied to data.\n        - The class supports serialization and deserialization of transforms.\n        - It provides methods for adding targets, setting deterministic behavior,\n          and checking data validity post-transform.\n    \"\"\"\n\n    _transforms_dict: dict[int, BasicTransform] | None = None\n    check_each_transform: tuple[DataProcessor, ...] | None = None\n    main_compose: bool = True\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        p: float,\n        mask_interpolation: int | None = None,\n        seed: int | None = None,\n        save_applied_params: bool = False,\n    ):\n        if isinstance(transforms, (BaseCompose, BasicTransform)):\n            warnings.warn(\n                \"transforms is single transform, but a sequence is expected! Transform will be wrapped into list.\",\n                stacklevel=2,\n            )\n            transforms = [transforms]\n\n        self.transforms = transforms\n        self.p = p\n\n        self.replay_mode = False\n        self._additional_targets: dict[str, str] = {}\n        self._available_keys: set[str] = set()\n        self.processors: dict[str, BboxProcessor | KeypointsProcessor] = {}\n        self._set_keys()\n        self.set_mask_interpolation(mask_interpolation)\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n        self.set_random_seed(seed)\n        self.save_applied_params = save_applied_params\n\n    def _track_transform_params(self, transform: TransformType, data: dict[str, Any]) -> None:\n        \"\"\"Track transform parameters if tracking is enabled.\"\"\"\n        if \"applied_transforms\" in data and hasattr(transform, \"params\") and transform.params:\n            data[\"applied_transforms\"].append((transform.__class__.__name__, transform.params.copy()))\n\n    def set_random_state(\n        self,\n        random_generator: np.random.Generator,\n        py_random: random.Random,\n    ) -> None:\n        \"\"\"Set random state directly from generators.\n\n        Args:\n            random_generator: numpy random generator to use\n            py_random: python random generator to use\n        \"\"\"\n        self.random_generator = random_generator\n        self.py_random = py_random\n\n        # Propagate both random states to all transforms\n        for transform in self.transforms:\n            if isinstance(transform, (BasicTransform, BaseCompose)):\n                transform.set_random_state(random_generator, py_random)\n\n    def set_random_seed(self, seed: int | None) -> None:\n        \"\"\"Set random state from seed.\n\n        Args:\n            seed: Random seed to use\n        \"\"\"\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n\n        # Propagate seed to all transforms\n        for transform in self.transforms:\n            if isinstance(transform, (BasicTransform, BaseCompose)):\n                transform.set_random_seed(seed)\n\n    def set_mask_interpolation(self, mask_interpolation: int | None) -> None:\n        self.mask_interpolation = mask_interpolation\n        self._set_mask_interpolation_recursive(self.transforms)\n\n    def _set_mask_interpolation_recursive(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BasicTransform):\n                if hasattr(transform, \"mask_interpolation\") and self.mask_interpolation is not None:\n                    transform.mask_interpolation = self.mask_interpolation\n            elif isinstance(transform, BaseCompose):\n                transform.set_mask_interpolation(self.mask_interpolation)\n\n    def __iter__(self) -> Iterator[TransformType]:\n        return iter(self.transforms)\n\n    def __len__(self) -> int:\n        return len(self.transforms)\n\n    def __call__(self, *args: Any, **data: Any) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def __getitem__(self, item: int) -> TransformType:\n        return self.transforms[item]\n\n    def __repr__(self) -> str:\n        return self.indented_repr()\n\n    @property\n    def additional_targets(self) -> dict[str, str]:\n        return self._additional_targets\n\n    @property\n    def available_keys(self) -> set[str]:\n        return self._available_keys\n\n    def indented_repr(self, indent: int = REPR_INDENT_STEP) -> str:\n        args = {k: v for k, v in self.to_dict_private().items() if not (k.startswith(\"__\") or k == \"transforms\")}\n        repr_string = self.__class__.__name__ + \"([\"\n        for t in self.transforms:\n            repr_string += \"\\n\"\n            t_repr = t.indented_repr(indent + REPR_INDENT_STEP) if hasattr(t, \"indented_repr\") else repr(t)\n            repr_string += \" \" * indent + t_repr + \",\"\n        repr_string += \"\\n\" + \" \" * (indent - REPR_INDENT_STEP) + f\"], {format_args(args)})\"\n        return repr_string\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    def to_dict_private(self) -> dict[str, Any]:\n        return {\n            \"__class_fullname__\": self.get_class_fullname(),\n            \"p\": self.p,\n            \"transforms\": [t.to_dict_private() for t in self.transforms],\n        }\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        return {\n            \"__class_fullname__\": self.get_class_fullname(),\n            \"id\": id(self),\n            \"params\": None,\n            \"transforms\": [t.get_dict_with_id() for t in self.transforms],\n        }\n\n    def add_targets(self, additional_targets: dict[str, str] | None) -> None:\n        if additional_targets:\n            for k, v in additional_targets.items():\n                if k in self._additional_targets and v != self._additional_targets[k]:\n                    raise ValueError(\n                        f\"Trying to overwrite existed additional targets. \"\n                        f\"Key={k} Exists={self._additional_targets[k]} New value: {v}\",\n                    )\n            self._additional_targets.update(additional_targets)\n            for t in self.transforms:\n                t.add_targets(additional_targets)\n            for proc in self.processors.values():\n                proc.add_targets(additional_targets)\n        self._set_keys()\n\n    def _set_keys(self) -> None:\n        \"\"\"Set _available_keys\"\"\"\n        self._available_keys.update(self._additional_targets.keys())\n        for t in self.transforms:\n            self._available_keys.update(t.available_keys)\n            if hasattr(t, \"targets_as_params\"):\n                self._available_keys.update(t.targets_as_params)\n        if self.processors:\n            self._available_keys.update([\"labels\"])\n            for proc in self.processors.values():\n                if proc.default_data_name not in self._available_keys:  # if no transform to process this data\n                    warnings.warn(\n                        f\"Got processor for {proc.default_data_name}, but no transform to process it.\",\n                        stacklevel=2,\n                    )\n                self._available_keys.update(proc.data_fields)\n                if proc.params.label_fields:\n                    self._available_keys.update(proc.params.label_fields)\n\n    def set_deterministic(self, flag: bool, save_key: str = \"replay\") -> None:\n        for t in self.transforms:\n            t.set_deterministic(flag, save_key)\n\n    def check_data_post_transform(self, data: Any) -> dict[str, Any]:\n        if self.check_each_transform:\n            image_shape = get_shape(data[\"image\"])\n\n            for proc in self.check_each_transform:\n                for data_name in data:\n                    if data_name in proc.data_fields or (\n                        data_name in self._additional_targets\n                        and self._additional_targets[data_name] in proc.data_fields\n                    ):\n                        data[data_name] = proc.filter(data[data_name], image_shape)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.Compose","title":"class Compose (transforms, bbox_params=None, keypoint_params=None, additional_targets=None, p=1.0, is_check_shapes=True, strict=True, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub]","text":"

Compose multiple transforms together and apply them sequentially to input data.

This class allows you to chain multiple image augmentation transforms and apply them in a specified order. It also handles bounding box and keypoint transformations if the appropriate parameters are provided.

Parameters:

Name Type Description transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to apply.

bbox_params Union[dict, BboxParams, None]

Parameters for bounding box transforms. Can be a dict of params or a BboxParams object. Default is None.

keypoint_params Union[dict, KeypointParams, None]

Parameters for keypoint transforms. Can be a dict of params or a KeypointParams object. Default is None.

additional_targets Dict[str, str]

A dictionary mapping additional target names to their types. For example, {'image2': 'image'}. Default is None.

p float

Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.

is_check_shapes bool

If True, checks consistency of shapes for image/mask/masks on each call. Disable only if you are sure about your data consistency. Default is True.

strict bool

If True, raises an error on unknown input keys. If False, ignores them. Default is True.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

seed int

Random seed. Default is None.

save_applied_params bool

If True, saves the applied parameters of each transform. Default is False.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.RandomCrop(width=256, height=256),\n...     A.HorizontalFlip(p=0.5),\n...     A.RandomBrightnessContrast(p=0.2),\n... ])\n>>> transformed = transform(image=image)\n

Note

  • The class checks the validity of input data and shapes if is_check_args and is_check_shapes are True.
  • When bbox_params or keypoint_params are provided, it sets up the corresponding processors.
  • The transform can handle additional targets specified in the additional_targets dictionary.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class Compose(BaseCompose, HubMixin):\n    \"\"\"Compose multiple transforms together and apply them sequentially to input data.\n\n    This class allows you to chain multiple image augmentation transforms and apply them\n    in a specified order. It also handles bounding box and keypoint transformations if\n    the appropriate parameters are provided.\n\n    Args:\n        transforms (List[Union[BasicTransform, BaseCompose]]): A list of transforms to apply.\n        bbox_params (Union[dict, BboxParams, None]): Parameters for bounding box transforms.\n            Can be a dict of params or a BboxParams object. Default is None.\n        keypoint_params (Union[dict, KeypointParams, None]): Parameters for keypoint transforms.\n            Can be a dict of params or a KeypointParams object. Default is None.\n        additional_targets (Dict[str, str], optional): A dictionary mapping additional target names\n            to their types. For example, {'image2': 'image'}. Default is None.\n        p (float): Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.\n        is_check_shapes (bool): If True, checks consistency of shapes for image/mask/masks on each call.\n            Disable only if you are sure about your data consistency. Default is True.\n        strict (bool): If True, raises an error on unknown input keys. If False, ignores them. Default is True.\n        mask_interpolation (int, optional): Interpolation method for mask transforms. When defined,\n            it overrides the interpolation method specified in individual transforms. Default is None.\n        seed (int, optional): Random seed. Default is None.\n        save_applied_params (bool): If True, saves the applied parameters of each transform. Default is False.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.RandomCrop(width=256, height=256),\n        ...     A.HorizontalFlip(p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.2),\n        ... ])\n        >>> transformed = transform(image=image)\n\n    Note:\n        - The class checks the validity of input data and shapes if is_check_args and is_check_shapes are True.\n        - When bbox_params or keypoint_params are provided, it sets up the corresponding processors.\n        - The transform can handle additional targets specified in the additional_targets dictionary.\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        bbox_params: dict[str, Any] | BboxParams | None = None,\n        keypoint_params: dict[str, Any] | KeypointParams | None = None,\n        additional_targets: dict[str, str] | None = None,\n        p: float = 1.0,\n        is_check_shapes: bool = True,\n        strict: bool = True,\n        mask_interpolation: int | None = None,\n        seed: int | None = None,\n        save_applied_params: bool = False,\n    ):\n        super().__init__(\n            transforms=transforms,\n            p=p,\n            mask_interpolation=mask_interpolation,\n            seed=seed,\n            save_applied_params=save_applied_params,\n        )\n\n        if bbox_params:\n            if isinstance(bbox_params, dict):\n                b_params = BboxParams(**bbox_params)\n            elif isinstance(bbox_params, BboxParams):\n                b_params = bbox_params\n            else:\n                msg = \"unknown format of bbox_params, please use `dict` or `BboxParams`\"\n                raise ValueError(msg)\n            self.processors[\"bboxes\"] = BboxProcessor(b_params)\n\n        if keypoint_params:\n            if isinstance(keypoint_params, dict):\n                k_params = KeypointParams(**keypoint_params)\n            elif isinstance(keypoint_params, KeypointParams):\n                k_params = keypoint_params\n            else:\n                msg = \"unknown format of keypoint_params, please use `dict` or `KeypointParams`\"\n                raise ValueError(msg)\n            self.processors[\"keypoints\"] = KeypointsProcessor(k_params)\n\n        for proc in self.processors.values():\n            proc.ensure_transforms_valid(self.transforms)\n\n        self.add_targets(additional_targets)\n        if not self.transforms:  # if no transforms -> do nothing, all keys will be available\n            self._available_keys.update(AVAILABLE_KEYS)\n\n        self.is_check_args = True\n        self.strict = strict\n\n        self.is_check_shapes = is_check_shapes\n        self.check_each_transform = tuple(  # processors that checks after each transform\n            proc for proc in self.processors.values() if getattr(proc.params, \"check_each_transform\", False)\n        )\n        self._set_check_args_for_transforms(self.transforms)\n\n        self._set_processors_for_transforms(self.transforms)\n\n        self.save_applied_params = save_applied_params\n        self._images_was_list = False\n        self._masks_was_list = False\n\n    def _set_processors_for_transforms(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BasicTransform):\n                if hasattr(transform, \"set_processors\"):\n                    transform.set_processors(self.processors)\n            elif isinstance(transform, BaseCompose):\n                self._set_processors_for_transforms(transform.transforms)\n\n    def _set_check_args_for_transforms(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BaseCompose):\n                self._set_check_args_for_transforms(transform.transforms)\n                transform.check_each_transform = self.check_each_transform\n                transform.processors = self.processors\n            if isinstance(transform, Compose):\n                transform.disable_check_args_private()\n\n    def disable_check_args_private(self) -> None:\n        self.is_check_args = False\n        self.strict = False\n        self.main_compose = False\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if args:\n            msg = \"You have to pass data to augmentations as named arguments, for example: aug(image=image)\"\n            raise KeyError(msg)\n\n        if not isinstance(force_apply, (bool, int)):\n            msg = \"force_apply must have bool or int type\"\n            raise TypeError(msg)\n\n        # Initialize applied_transforms only in top-level Compose if requested\n        if self.save_applied_params and self.main_compose:\n            data[\"applied_transforms\"] = []\n\n        need_to_run = force_apply or self.py_random.random() < self.p\n        if not need_to_run:\n            return data\n\n        self.preprocess(data)\n\n        for t in self.transforms:\n            data = t(**data)\n            self._track_transform_params(t, data)\n            data = self.check_data_post_transform(data)\n\n        return self.postprocess(data)\n\n    def preprocess(self, data: Any) -> None:\n        \"\"\"Preprocess input data before applying transforms.\"\"\"\n        self._validate_data(data)\n        self._preprocess_processors(data)\n        self._preprocess_arrays(data)\n\n    def _validate_data(self, data: dict[str, Any]) -> None:\n        \"\"\"Validate input data keys and arguments.\"\"\"\n        if not self.strict:\n            return\n\n        for data_name in data:\n            if not self._is_valid_key(data_name):\n                raise ValueError(f\"Key {data_name} is not in available keys.\")\n\n        if self.is_check_args:\n            self._check_args(**data)\n\n    def _is_valid_key(self, key: str) -> bool:\n        \"\"\"Check if the key is valid for processing.\"\"\"\n        return key in self._available_keys or key in MASK_KEYS or key in IMAGE_KEYS or key == \"applied_transforms\"\n\n    def _preprocess_processors(self, data: dict[str, Any]) -> None:\n        \"\"\"Run preprocessors if this is the main compose.\"\"\"\n        if not self.main_compose:\n            return\n\n        for processor in self.processors.values():\n            processor.ensure_data_valid(data)\n        for processor in self.processors.values():\n            processor.preprocess(data)\n\n    def _preprocess_arrays(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert lists to numpy arrays for images and masks.\"\"\"\n        self._preprocess_images(data)\n        self._preprocess_masks(data)\n\n    def _preprocess_images(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert image lists to numpy arrays.\"\"\"\n        if \"images\" not in data:\n            return\n\n        if isinstance(data[\"images\"], (list, tuple)):\n            self._images_was_list = True\n            data[\"images\"] = np.stack(data[\"images\"])\n        else:\n            self._images_was_list = False\n\n    def _preprocess_masks(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert mask lists to numpy arrays.\"\"\"\n        if \"masks\" not in data:\n            return\n\n        if isinstance(data[\"masks\"], (list, tuple)):\n            self._masks_was_list = True\n            data[\"masks\"] = np.stack(data[\"masks\"])\n        else:\n            self._masks_was_list = False\n\n    def postprocess(self, data: dict[str, Any]) -> dict[str, Any]:\n        if self.main_compose:\n            for p in self.processors.values():\n                p.postprocess(data)\n\n            # Convert back to list if original input was a list\n            if \"images\" in data and self._images_was_list:\n                data[\"images\"] = list(data[\"images\"])\n\n            if \"masks\" in data and self._masks_was_list:\n                data[\"masks\"] = list(data[\"masks\"])\n\n        return data\n\n    def to_dict_private(self) -> dict[str, Any]:\n        dictionary = super().to_dict_private()\n        bbox_processor = self.processors.get(\"bboxes\")\n        keypoints_processor = self.processors.get(\"keypoints\")\n        dictionary.update(\n            {\n                \"bbox_params\": bbox_processor.params.to_dict_private() if bbox_processor else None,\n                \"keypoint_params\": (keypoints_processor.params.to_dict_private() if keypoints_processor else None),\n                \"additional_targets\": self.additional_targets,\n                \"is_check_shapes\": self.is_check_shapes,\n            },\n        )\n        return dictionary\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        dictionary = super().get_dict_with_id()\n        bbox_processor = self.processors.get(\"bboxes\")\n        keypoints_processor = self.processors.get(\"keypoints\")\n        dictionary.update(\n            {\n                \"bbox_params\": bbox_processor.params.to_dict_private() if bbox_processor else None,\n                \"keypoint_params\": (keypoints_processor.params.to_dict_private() if keypoints_processor else None),\n                \"additional_targets\": self.additional_targets,\n                \"params\": None,\n                \"is_check_shapes\": self.is_check_shapes,\n            },\n        )\n        return dictionary\n\n    @staticmethod\n    def _check_single_data(data_name: str, data: Any) -> tuple[int, int]:\n        if not isinstance(data, np.ndarray):\n            raise TypeError(f\"{data_name} must be numpy array type\")\n        return data.shape[:2]\n\n    @staticmethod\n    def _check_masks_data(data_name: str, data: Any) -> tuple[int, int]:\n        \"\"\"Check masks data format and return shape.\n\n        Args:\n            data_name: Name of the data field being checked\n            data: Input data in one of these formats:\n                - List of numpy arrays, each of shape (H, W) or (H, W, C)\n                - Numpy array of shape (N, H, W) or (N, H, W, C)\n\n        Returns:\n            tuple: (height, width) of the first mask\n\n        Raises:\n            TypeError: If data format is invalid\n        \"\"\"\n        if isinstance(data, np.ndarray):\n            if data.ndim not in [3, 4]:  # (N,H,W) or (N,H,W,C)\n                raise TypeError(f\"{data_name} as numpy array must be 3D or 4D\")\n            return data.shape[1:3]  # Return (H,W)\n\n        if isinstance(data, (list, tuple)):\n            if not data:\n                raise ValueError(f\"{data_name} cannot be empty\")\n            if not all(isinstance(m, np.ndarray) for m in data):\n                raise TypeError(f\"All elements in {data_name} must be numpy arrays\")\n            if any(m.ndim not in [2, 3] for m in data):\n                raise TypeError(f\"All masks in {data_name} must be 2D or 3D numpy arrays\")\n            return data[0].shape[:2]\n\n        raise TypeError(f\"{data_name} must be either a numpy array or a sequence of numpy arrays\")\n\n    @staticmethod\n    def _check_multi_data(data_name: str, data: Any) -> tuple[int, int]:\n        \"\"\"Check multi-image data format and return shape.\n\n        Args:\n            data_name: Name of the data field being checked\n            data: Input data in one of these formats:\n                - List-like of numpy arrays\n                - Numpy array of shape (N, H, W, C) or (N, H, W)\n\n        Returns:\n            tuple: (height, width) of the first image\n\n        Raises:\n            TypeError: If data format is invalid\n        \"\"\"\n        if isinstance(data, np.ndarray):\n            if data.ndim not in {3, 4}:  # (N,H,W) or (N,H,W,C)\n                raise TypeError(f\"{data_name} as numpy array must be 3D or 4D\")\n            return data.shape[1:3]  # Return (H,W)\n\n        if not isinstance(data, Sequence) or not isinstance(data[0], np.ndarray):\n            raise TypeError(f\"{data_name} must be either a numpy array or a list of numpy arrays\")\n        return data[0].shape[:2]\n\n    @staticmethod\n    def _check_bbox_keypoint_params(internal_data_name: str, processors: dict[str, Any]) -> None:\n        if internal_data_name in CHECK_BBOX_PARAM and processors.get(\"bboxes\") is None:\n            raise ValueError(\"bbox_params must be specified for bbox transformations\")\n        if internal_data_name in CHECK_KEYPOINTS_PARAM and processors.get(\"keypoints\") is None:\n            raise ValueError(\"keypoints_params must be specified for keypoint transformations\")\n\n    @staticmethod\n    def _check_shapes(shapes: list[tuple[int, ...]], is_check_shapes: bool) -> None:\n        if is_check_shapes and shapes and shapes.count(shapes[0]) != len(shapes):\n            raise ValueError(\n                \"Height and Width of image, mask or masks should be equal. You can disable shapes check \"\n                \"by setting a parameter is_check_shapes=False of Compose class (do it only if you are sure \"\n                \"about your data consistency).\",\n            )\n\n    def _check_args(self, **kwargs: Any) -> None:\n        shapes = []  # For H,W checks\n        volume_shapes = []  # For D,H,W checks\n\n        for data_name, data in kwargs.items():\n            internal_name = self._additional_targets.get(data_name, data_name)\n\n            # For CHECKED_SINGLE, we must validate even if None\n            if internal_name in CHECKED_SINGLE:\n                if not isinstance(data, np.ndarray):\n                    raise TypeError(f\"{data_name} must be numpy array type\")\n                shapes.append(data.shape[:2])\n                continue\n\n            # Skip empty data or non-array/list inputs for other types\n            if data is None:\n                continue\n            if not isinstance(data, (np.ndarray, list)):\n                continue\n\n            self._check_bbox_keypoint_params(internal_name, self.processors)\n\n            shape = self._get_data_shape(data_name, internal_name, data)\n            if shape is None:\n                continue\n\n            # Handle different shape types\n            if internal_name in CHECKED_VOLUME | CHECKED_MASK3D:\n                shapes.append(shape[1:3])  # H,W from (D,H,W)\n                volume_shapes.append(shape[:3])  # D,H,W\n            elif internal_name in {\"volumes\", \"masks3d\"}:\n                shapes.append(shape[2:4])  # H,W from (N,D,H,W)\n                volume_shapes.append(shape[1:4])  # D,H,W from (N,D,H,W)\n            else:\n                shapes.append(shape[:2])  # H,W\n\n        self._check_shape_consistency(shapes, volume_shapes)\n\n    def _get_data_shape(self, data_name: str, internal_name: str, data: Any) -> tuple[int, ...] | None:\n        \"\"\"Get shape of data based on its type.\"\"\"\n        if internal_name in CHECKED_SINGLE:\n            if not isinstance(data, np.ndarray):\n                raise TypeError(f\"{data_name} must be numpy array type\")\n            return data.shape\n\n        if internal_name in CHECKED_VOLUME:\n            return self._check_volume_data(data_name, data)\n\n        if internal_name in CHECKED_MASK3D:\n            return self._check_mask3d_data(data_name, data)\n\n        if internal_name in CHECKED_MULTI:\n            if internal_name == \"masks\":\n                return self._check_masks_data(data_name, data)\n            if internal_name in {\"volumes\", \"masks3d\"}:  # Group these together\n                if not isinstance(data, np.ndarray):\n                    raise TypeError(f\"{data_name} must be numpy array type\")\n                if data.ndim not in {4, 5}:  # (N,D,H,W) or (N,D,H,W,C)\n                    raise TypeError(f\"{data_name} must be 4D or 5D array\")\n                return data.shape  # Return full shape\n            return self._check_multi_data(data_name, data)\n\n        return None\n\n    def _check_shape_consistency(self, shapes: list[tuple[int, ...]], volume_shapes: list[tuple[int, ...]]) -> None:\n        \"\"\"Check consistency of shapes.\"\"\"\n        # Check H,W consistency\n        self._check_shapes(shapes, self.is_check_shapes)\n\n        # Check D,H,W consistency for volumes and 3D masks\n        if self.is_check_shapes and volume_shapes and volume_shapes.count(volume_shapes[0]) != len(volume_shapes):\n            raise ValueError(\n                \"Depth, Height and Width of volume, mask3d, volumes and masks3d should be equal. \"\n                \"You can disable shapes check by setting is_check_shapes=False.\",\n            )\n\n    @staticmethod\n    def _check_volume_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        if data.ndim not in {3, 4}:  # (D,H,W) or (D,H,W,C)\n            raise TypeError(f\"{data_name} must be 3D or 4D array\")\n        return data.shape[:3]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_volumes_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        if data.ndim not in {4, 5}:  # (N,D,H,W) or (N,D,H,W,C)\n            raise TypeError(f\"{data_name} must be 4D or 5D array\")\n        return data.shape[1:4]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_mask3d_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        \"\"\"Check single volumetric mask data format and return shape.\"\"\"\n        if data.ndim not in {3, 4}:  # (D,H,W) or (D,H,W,C)\n            raise TypeError(f\"{data_name} must be 3D or 4D array\")\n        return data.shape[:3]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_masks3d_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        \"\"\"Check multiple volumetric masks data format and return shape.\"\"\"\n        if data.ndim not in [4, 5]:  # (N,D,H,W) or (N,D,H,W,C)\n            raise TypeError(f\"{data_name} must be 4D or 5D array\")\n        return data.shape[1:4]  # Return (D,H,W)\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.OneOf","title":"class OneOf (transforms, p=0.5) [view source on GitHub]","text":"

Select one of transforms to apply. Selected transform will be called with force_apply=True. Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.

Parameters:

Name Type Description transforms list

list of transformations to compose.

p float

probability of applying selected transform. Default: 0.5.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class OneOf(BaseCompose):\n    \"\"\"Select one of transforms to apply. Selected transform will be called with `force_apply=True`.\n    Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.\n\n    Args:\n        transforms (list): list of transformations to compose.\n        p (float): probability of applying selected transform. Default: 0.5.\n\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, p: float = 0.5):\n        super().__init__(transforms=transforms, p=p)\n        transforms_ps = [t.p for t in self.transforms]\n        s = sum(transforms_ps)\n        self.transforms_ps = [t / s for t in transforms_ps]\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n            return data\n\n        if self.transforms_ps and (force_apply or self.py_random.random() < self.p):\n            idx: int = self.random_generator.choice(len(self.transforms), p=self.transforms_ps)\n            t = self.transforms[idx]\n            data = t(force_apply=True, **data)\n            self._track_transform_params(t, data)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.OneOrOther","title":"class OneOrOther (first=None, second=None, transforms=None, p=0.5) [view source on GitHub]","text":"

Select one or another transform to apply. Selected transform will be called with force_apply=True.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class OneOrOther(BaseCompose):\n    \"\"\"Select one or another transform to apply. Selected transform will be called with `force_apply=True`.\"\"\"\n\n    def __init__(\n        self,\n        first: TransformType | None = None,\n        second: TransformType | None = None,\n        transforms: TransformsSeqType | None = None,\n        p: float = 0.5,\n    ):\n        if transforms is None:\n            if first is None or second is None:\n                msg = \"You must set both first and second or set transforms argument.\"\n                raise ValueError(msg)\n            transforms = [first, second]\n        super().__init__(transforms, p)\n        if len(self.transforms) != NUM_ONEOF_TRANSFORMS:\n            warnings.warn(\"Length of transforms is not equal to 2.\", stacklevel=2)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n                self._track_transform_params(t, data)\n            return data\n\n        if self.py_random.random() < self.p:\n            return self.transforms[0](force_apply=True, **data)\n\n        return self.transforms[-1](force_apply=True, **data)\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.RandomOrder","title":"class RandomOrder (transforms, n=1, replace=False, p=1) [view source on GitHub]","text":"

Apply a random subset of transforms from the given list in a random order.

The RandomOrder class allows you to select a specified number of transforms from a list and apply them to the input data in a random order. This is useful for creating more diverse augmentation pipelines where the order of transformations can vary, potentially leading to different results.

Attributes:

Name Type Description transforms TransformsSeqType

A list of transformations to choose from.

n int

The number of transforms to apply. If n is greater than the number of available transforms and replace is False, n will be set to the number of available transforms.

replace bool

Whether to sample transforms with replacement. If True, the same transform can be selected multiple times. Default is False.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.RandomOrder([\n...     A.HorizontalFlip(p=1),\n...     A.VerticalFlip(p=1),\n...     A.RandomBrightnessContrast(p=1),\n... ], n=2, replace=False, p=0.5)\n>>> # This will apply 2 out of the 3 transforms in a random order with 50% probability\n

Note

  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.
  • The random order of transforms will not be replayed in ReplayCompose.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class RandomOrder(SomeOf):\n    \"\"\"Apply a random subset of transforms from the given list in a random order.\n\n    The `RandomOrder` class allows you to select a specified number of transforms from a list and apply them\n    to the input data in a random order. This is useful for creating more diverse augmentation pipelines\n    where the order of transformations can vary, potentially leading to different results.\n\n    Attributes:\n        transforms (TransformsSeqType): A list of transformations to choose from.\n        n (int): The number of transforms to apply. If `n` is greater than the number of available transforms\n                 and `replace` is False, `n` will be set to the number of available transforms.\n        replace (bool): Whether to sample transforms with replacement. If True, the same transform can be\n                        selected multiple times. Default is False.\n        p (float): Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.RandomOrder([\n        ...     A.HorizontalFlip(p=1),\n        ...     A.VerticalFlip(p=1),\n        ...     A.RandomBrightnessContrast(p=1),\n        ... ], n=2, replace=False, p=0.5)\n        >>> # This will apply 2 out of the 3 transforms in a random order with 50% probability\n\n    Note:\n        - The probabilities of individual transforms are used as weights for sampling.\n        - When `replace` is True, the same transform can be selected multiple times.\n        - The random order of transforms will not be replayed in `ReplayCompose`.\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, n: int = 1, replace: bool = False, p: float = 1):\n        super().__init__(transforms=transforms, n=n, replace=replace, p=p)\n\n    def _get_idx(self) -> np.ndarray[np.int_]:\n        return self.random_generator.choice(\n            len(self.transforms),\n            size=self.n,\n            replace=self.replace,\n            p=self.transforms_ps,\n        )\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.SelectiveChannelTransform","title":"class SelectiveChannelTransform (transforms, channels=(0, 1, 2), p=1.0) [view source on GitHub]","text":"

A transformation class to apply specified transforms to selected channels of an image.

This class extends BaseCompose to allow selective application of transformations to specified image channels. It extracts the selected channels, applies the transformations, and then reinserts the transformed channels back into their original positions in the image.

Parameters:

Name Type Description transforms TransformsSeqType

A sequence of transformations (from Albumentations) to be applied to the specified channels.

channels Sequence[int]

A sequence of integers specifying the indices of the channels to which the transforms should be applied.

p float

Probability that the transform will be applied; the default is 1.0 (always apply).

Methods

call(args, *kwargs): Applies the transforms to the image according to the specified channels. The input data should include 'image' key with the image array.

Returns:

Type Description dict[str, Any]

The transformed data dictionary, which includes the transformed 'image' key.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class SelectiveChannelTransform(BaseCompose):\n    \"\"\"A transformation class to apply specified transforms to selected channels of an image.\n\n    This class extends BaseCompose to allow selective application of transformations to\n    specified image channels. It extracts the selected channels, applies the transformations,\n    and then reinserts the transformed channels back into their original positions in the image.\n\n    Parameters:\n        transforms (TransformsSeqType):\n            A sequence of transformations (from Albumentations) to be applied to the specified channels.\n        channels (Sequence[int]):\n            A sequence of integers specifying the indices of the channels to which the transforms should be applied.\n        p (float):\n            Probability that the transform will be applied; the default is 1.0 (always apply).\n\n    Methods:\n        __call__(*args, **kwargs):\n            Applies the transforms to the image according to the specified channels.\n            The input data should include 'image' key with the image array.\n\n    Returns:\n        dict[str, Any]: The transformed data dictionary, which includes the transformed 'image' key.\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        channels: Sequence[int] = (0, 1, 2),\n        p: float = 1.0,\n    ) -> None:\n        super().__init__(transforms, p)\n        self.channels = channels\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if force_apply or self.py_random.random() < self.p:\n            image = data[\"image\"]\n\n            selected_channels = image[:, :, self.channels]\n            sub_image = np.ascontiguousarray(selected_channels)\n\n            for t in self.transforms:\n                sub_image = t(image=sub_image)[\"image\"]\n                self._track_transform_params(t, sub_image)\n\n            transformed_channels = cv2.split(sub_image)\n            output_img = image.copy()\n\n            for idx, channel in zip(self.channels, transformed_channels):\n                output_img[:, :, idx] = channel\n\n            data[\"image\"] = np.ascontiguousarray(output_img)\n\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.Sequential","title":"class Sequential (transforms, p=0.5) [view source on GitHub]","text":"

Sequentially applies all transforms to targets.

Note

This transform is not intended to be a replacement for Compose. Instead, it should be used inside Compose the same way OneOf or OneOrOther are used. For instance, you can combine OneOf with Sequential to create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly chose sequence to input data (see the Example section for an example definition of such pipeline).

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n>>>    A.OneOf([\n>>>        A.Sequential([\n>>>            A.HorizontalFlip(p=0.5),\n>>>            A.ShiftScaleRotate(p=0.5),\n>>>        ]),\n>>>        A.Sequential([\n>>>            A.VerticalFlip(p=0.5),\n>>>            A.RandomBrightnessContrast(p=0.5),\n>>>        ]),\n>>>    ], p=1)\n>>> ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class Sequential(BaseCompose):\n    \"\"\"Sequentially applies all transforms to targets.\n\n    Note:\n        This transform is not intended to be a replacement for `Compose`. Instead, it should be used inside `Compose`\n        the same way `OneOf` or `OneOrOther` are used. For instance, you can combine `OneOf` with `Sequential` to\n        create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly\n        chose sequence to input data (see the `Example` section for an example definition of such pipeline).\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        >>>    A.OneOf([\n        >>>        A.Sequential([\n        >>>            A.HorizontalFlip(p=0.5),\n        >>>            A.ShiftScaleRotate(p=0.5),\n        >>>        ]),\n        >>>        A.Sequential([\n        >>>            A.VerticalFlip(p=0.5),\n        >>>            A.RandomBrightnessContrast(p=0.5),\n        >>>        ]),\n        >>>    ], p=1)\n        >>> ])\n\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, p: float = 0.5):\n        super().__init__(transforms, p)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode or force_apply or self.py_random.random() < self.p:\n            for t in self.transforms:\n                data = t(**data)\n                self._track_transform_params(t, data)\n                data = self.check_data_post_transform(data)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.SomeOf","title":"class SomeOf (transforms, n=1, replace=False, p=1) [view source on GitHub]","text":"

Apply a random subset of transforms from the given list.

This class selects a specified number of transforms from the provided list and applies them to the input data. The selection can be done with or without replacement, allowing for the same transform to be potentially applied multiple times.

Parameters:

Name Type Description transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to choose from.

n int

The number of transforms to apply. If greater than the number of transforms and replace=False, it will be set to the number of transforms.

replace bool

Whether to sample transforms with replacement. Default is True.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

Note

  • If n is greater than the number of transforms and replace is False, n will be set to the number of transforms with a warning.
  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.SomeOf([\n...     A.HorizontalFlip(p=1),\n...     A.VerticalFlip(p=1),\n...     A.RandomBrightnessContrast(p=1),\n... ], n=2, replace=False, p=0.5)\n>>> # This will apply 2 out of the 3 transforms with 50% probability\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class SomeOf(BaseCompose):\n    \"\"\"Apply a random subset of transforms from the given list.\n\n    This class selects a specified number of transforms from the provided list\n    and applies them to the input data. The selection can be done with or without\n    replacement, allowing for the same transform to be potentially applied multiple times.\n\n    Args:\n        transforms (List[Union[BasicTransform, BaseCompose]]): A list of transforms to choose from.\n        n (int): The number of transforms to apply. If greater than the number of\n                 transforms and replace=False, it will be set to the number of transforms.\n        replace (bool): Whether to sample transforms with replacement. Default is True.\n        p (float): Probability of applying the selected transforms. Should be in the range [0, 1].\n                   Default is 1.0.\n        mask_interpolation (int, optional): Interpolation method for mask transforms.\n                                            When defined, it overrides the interpolation method\n                                            specified in individual transforms. Default is None.\n\n    Note:\n        - If `n` is greater than the number of transforms and `replace` is False,\n          `n` will be set to the number of transforms with a warning.\n        - The probabilities of individual transforms are used as weights for sampling.\n        - When `replace` is True, the same transform can be selected multiple times.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.SomeOf([\n        ...     A.HorizontalFlip(p=1),\n        ...     A.VerticalFlip(p=1),\n        ...     A.RandomBrightnessContrast(p=1),\n        ... ], n=2, replace=False, p=0.5)\n        >>> # This will apply 2 out of the 3 transforms with 50% probability\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, n: int = 1, replace: bool = False, p: float = 1):\n        super().__init__(transforms, p)\n        self.n = n\n        if not replace and n > len(self.transforms):\n            self.n = len(self.transforms)\n            warnings.warn(\n                f\"`n` is greater than number of transforms. `n` will be set to {self.n}.\",\n                UserWarning,\n                stacklevel=2,\n            )\n        self.replace = replace\n        transforms_ps = [t.p for t in self.transforms]\n        s = sum(transforms_ps)\n        self.transforms_ps = [t / s for t in transforms_ps]\n\n    def __call__(self, *arg: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n                data = self.check_data_post_transform(data)\n            return data\n\n        if self.transforms_ps and (force_apply or self.py_random.random() < self.p):\n            for i in self._get_idx():\n                t = self.transforms[i]\n                data = t(force_apply=True, **data)\n                self._track_transform_params(t, data)\n                data = self.check_data_post_transform(data)\n        return data\n\n    def _get_idx(self) -> np.ndarray[np.int_]:\n        idx = self.random_generator.choice(\n            len(self.transforms),\n            size=self.n,\n            replace=self.replace,\n            p=self.transforms_ps,\n        )\n        idx.sort()\n        return idx\n\n    def to_dict_private(self) -> dict[str, Any]:\n        dictionary = super().to_dict_private()\n        dictionary.update({\"n\": self.n, \"replace\": self.replace})\n        return dictionary\n
"},{"location":"api_reference/core/keypoints_utils/","title":"Helper functions for working with keypoints (augmentations.core.keypoints_utils)","text":""},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.KeypointParams","title":"class KeypointParams (format, label_fields=None, remove_invisible=True, angle_in_degrees=True, check_each_transform=True) [view source on GitHub]","text":"

Parameters of keypoints

Parameters:

Name Type Description format str

format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa'.

x - X coordinate,

y - Y coordinate

s - Keypoint scale

a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)

label_fields list

list of fields that are joined with keypoints, e.g labels. Should be same type as keypoints.

remove_invisible bool

to remove invisible points after transform or not

angle_in_degrees bool

angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints

check_each_transform bool

if True, then keypoints will be checked after each dual transform. Default: True

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py Python
class KeypointParams(Params):\n    \"\"\"Parameters of keypoints\n\n    Args:\n        format (str): format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa'.\n\n            x - X coordinate,\n\n            y - Y coordinate\n\n            s - Keypoint scale\n\n            a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)\n        label_fields (list): list of fields that are joined with keypoints, e.g labels.\n            Should be same type as keypoints.\n        remove_invisible (bool): to remove invisible points after transform or not\n        angle_in_degrees (bool): angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints\n        check_each_transform (bool): if `True`, then keypoints will be checked after each dual transform.\n            Default: `True`\n\n    \"\"\"\n\n    def __init__(\n        self,\n        format: str,  # noqa: A002\n        label_fields: Sequence[str] | None = None,\n        remove_invisible: bool = True,\n        angle_in_degrees: bool = True,\n        check_each_transform: bool = True,\n    ):\n        super().__init__(format, label_fields)\n        self.remove_invisible = remove_invisible\n        self.angle_in_degrees = angle_in_degrees\n        self.check_each_transform = check_each_transform\n\n    def to_dict_private(self) -> dict[str, Any]:\n        data = super().to_dict_private()\n        data.update(\n            {\n                \"remove_invisible\": self.remove_invisible,\n                \"angle_in_degrees\": self.angle_in_degrees,\n                \"check_each_transform\": self.check_each_transform,\n            },\n        )\n        return data\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return \"KeypointParams\"\n\n    def __repr__(self) -> str:\n        return (\n            f\"KeypointParams(format={self.format}, label_fields={self.label_fields},\"\n            f\" remove_invisible={self.remove_invisible}, angle_in_degrees={self.angle_in_degrees},\"\n            f\" check_each_transform={self.check_each_transform})\"\n        )\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.check_keypoints","title":"def check_keypoints (keypoints, image_shape) [view source on GitHub]","text":"

Check if keypoint coordinates are within valid ranges for the given image shape.

This function validates that: 1. All x-coordinates are within [0, width) 2. All y-coordinates are within [0, height) 3. If angles are present (i.e., keypoints have more than 2 columns), they are within the range [0, 2\u03c0)

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. Each row represents a keypoint with at least (x, y) coordinates. If present, the third column is assumed to be the angle.

image_shape Tuple[int, int]

The shape of the image (height, width).

Exceptions:

Type Description ValueError

If any keypoint coordinate is outside the valid range, or if any angle is invalid. The error message will detail which keypoints are invalid and why.

Note

  • The function assumes that keypoint coordinates are in absolute pixel values, not normalized.
  • Angles, if present, are assumed to be in radians.
  • The constant PAIR should be defined elsewhere in the module, typically as 2.
Source code in albumentations/core/keypoints_utils.py Python
def check_keypoints(keypoints: np.ndarray, image_shape: tuple[int, int]) -> None:\n    \"\"\"Check if keypoint coordinates are within valid ranges for the given image shape.\n\n    This function validates that:\n    1. All x-coordinates are within [0, width)\n    2. All y-coordinates are within [0, height)\n    3. If angles are present (i.e., keypoints have more than 2 columns),\n       they are within the range [0, 2\u03c0)\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.\n                                Each row represents a keypoint with at least (x, y) coordinates.\n                                If present, the third column is assumed to be the angle.\n        image_shape (Tuple[int, int]): The shape of the image (height, width).\n\n    Raises:\n        ValueError: If any keypoint coordinate is outside the valid range, or if any angle is invalid.\n                    The error message will detail which keypoints are invalid and why.\n\n    Note:\n        - The function assumes that keypoint coordinates are in absolute pixel values, not normalized.\n        - Angles, if present, are assumed to be in radians.\n        - The constant PAIR should be defined elsewhere in the module, typically as 2.\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Check x and y coordinates\n    x, y = keypoints[:, 0], keypoints[:, 1]\n    if np.any((x < 0) | (x >= width)) or np.any((y < 0) | (y >= height)):\n        invalid_x = np.where((x < 0) | (x >= width))[0]\n        invalid_y = np.where((y < 0) | (y >= height))[0]\n\n        error_messages = []\n\n        error_messages = [\n            f\"Expected {'x' if idx in invalid_x else 'y'} for keypoint {keypoints[idx]} to be \"\n            f\"in the range [0.0, {width if idx in invalid_x else height}], \"\n            f\"got {x[idx] if idx in invalid_x else y[idx]}.\"\n            for idx in sorted(set(invalid_x) | set(invalid_y))\n        ]\n\n        raise ValueError(\"\\n\".join(error_messages))\n\n    # Check angles\n    if keypoints.shape[1] > PAIR:\n        angles = keypoints[:, 2]\n        invalid_angles = np.where((angles < 0) | (angles >= 2 * math.pi))[0]\n        if len(invalid_angles) > 0:\n            error_messages = [\n                f\"Keypoint angle must be in range [0, 2 * PI). Got: {angles[idx]} for keypoint {keypoints[idx]}\"\n                for idx in invalid_angles\n            ]\n            raise ValueError(\"\\n\".join(error_messages))\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.convert_keypoints_from_albumentations","title":"def convert_keypoints_from_albumentations (keypoints, target_format, image_shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]","text":"

Convert keypoints from Albumentations format to various other formats.

This function takes keypoints in the standard Albumentations format [x, y, angle, scale] and converts them to the specified target format.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints in Albumentations format with shape (N, 4+), where N is the number of keypoints. Each row represents a keypoint [x, y, angle, scale, ...].

target_format Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"]

The desired output format. - \"xy\": [x, y] - \"yx\": [y, x] - \"xya\": [x, y, angle] - \"xys\": [x, y, scale] - \"xyas\": [x, y, angle, scale] - \"xysa\": [x, y, scale, angle]

image_shape tuple[int, int]

The shape of the image (height, width).

check_validity bool

If True, check if the keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert output angles to degrees. If False, angles remain in radians. Defaults to True.

Returns:

Type Description np.ndarray

Array of keypoints in the specified target format with shape (N, 2+). Any additional columns from the input keypoints beyond the first 4 are preserved and appended after the converted columns.

Exceptions:

Type Description ValueError

If the target_format is not one of the supported formats.

Note

  • Input angles are assumed to be in the range [0, 2\u03c0) radians.
  • If the input keypoints have additional columns beyond the first 4, these columns are preserved in the output.
  • The constant NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS should be defined elsewhere in the module, typically as 4.
Source code in albumentations/core/keypoints_utils.py Python
def convert_keypoints_from_albumentations(\n    keypoints: np.ndarray,\n    target_format: Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"],\n    image_shape: tuple[int, int],\n    check_validity: bool = False,\n    angle_in_degrees: bool = True,\n) -> np.ndarray:\n    \"\"\"Convert keypoints from Albumentations format to various other formats.\n\n    This function takes keypoints in the standard Albumentations format [x, y, angle, scale]\n    and converts them to the specified target format.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints in Albumentations format with shape (N, 4+),\n                                where N is the number of keypoints. Each row represents a keypoint\n                                [x, y, angle, scale, ...].\n        target_format (Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"]): The desired output format.\n            - \"xy\": [x, y]\n            - \"yx\": [y, x]\n            - \"xya\": [x, y, angle]\n            - \"xys\": [x, y, scale]\n            - \"xyas\": [x, y, angle, scale]\n            - \"xysa\": [x, y, scale, angle]\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        check_validity (bool, optional): If True, check if the keypoints are within the image boundaries.\n                                         Defaults to False.\n        angle_in_degrees (bool, optional): If True, convert output angles to degrees.\n                                           If False, angles remain in radians.\n                                           Defaults to True.\n\n    Returns:\n        np.ndarray: Array of keypoints in the specified target format with shape (N, 2+).\n                    Any additional columns from the input keypoints beyond the first 4\n                    are preserved and appended after the converted columns.\n\n    Raises:\n        ValueError: If the target_format is not one of the supported formats.\n\n    Note:\n        - Input angles are assumed to be in the range [0, 2\u03c0) radians.\n        - If the input keypoints have additional columns beyond the first 4,\n          these columns are preserved in the output.\n        - The constant NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS should be defined\n          elsewhere in the module, typically as 4.\n    \"\"\"\n    if target_format not in keypoint_formats:\n        raise ValueError(f\"Unknown target_format {target_format}. Supported formats are: {keypoint_formats}\")\n\n    x, y, angle, scale = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2], keypoints[:, 3]\n    angle = angle_to_2pi_range(angle)\n\n    if check_validity:\n        check_keypoints(np.column_stack((x, y, angle, scale)), image_shape)\n\n    if angle_in_degrees:\n        angle = np.degrees(angle)\n\n    format_to_columns = {\n        \"xy\": [x, y],\n        \"yx\": [y, x],\n        \"xya\": [x, y, angle],\n        \"xys\": [x, y, scale],\n        \"xyas\": [x, y, angle, scale],\n        \"xysa\": [x, y, scale, angle],\n    }\n\n    result = np.column_stack(format_to_columns[target_format])\n\n    # Add any additional columns from the original keypoints\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack((result, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]))\n\n    return result\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.convert_keypoints_to_albumentations","title":"def convert_keypoints_to_albumentations (keypoints, source_format, image_shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]","text":"

Convert keypoints from various formats to the Albumentations format.

This function takes keypoints in different formats and converts them to the standard Albumentations format: [x, y, angle, scale]. If the input format doesn't include angle or scale, these values are set to 0.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. The number of columns depends on the source_format.

source_format Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"]

The format of the input keypoints. - \"xy\": [x, y] - \"yx\": [y, x] - \"xya\": [x, y, angle] - \"xys\": [x, y, scale] - \"xyas\": [x, y, angle, scale] - \"xysa\": [x, y, scale, angle]

image_shape tuple[int, int]

The shape of the image (height, width).

check_validity bool

If True, check if the converted keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert input angles from degrees to radians. Defaults to True.

Returns:

Type Description np.ndarray

Array of keypoints in Albumentations format [x, y, angle, scale] with shape (N, 4+). Any additional columns from the input keypoints are preserved and appended after the first 4 columns.

Exceptions:

Type Description ValueError

If the source_format is not one of the supported formats.

Note

  • Angles are converted to the range [0, 2\u03c0) radians.
  • If the input keypoints have additional columns beyond what's specified in the source_format, these columns are preserved in the output.
Source code in albumentations/core/keypoints_utils.py Python
def convert_keypoints_to_albumentations(\n    keypoints: np.ndarray,\n    source_format: Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"],\n    image_shape: tuple[int, int],\n    check_validity: bool = False,\n    angle_in_degrees: bool = True,\n) -> np.ndarray:\n    \"\"\"Convert keypoints from various formats to the Albumentations format.\n\n    This function takes keypoints in different formats and converts them to the standard\n    Albumentations format: [x, y, angle, scale]. If the input format doesn't include\n    angle or scale, these values are set to 0.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.\n                                The number of columns depends on the source_format.\n        source_format (Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\"]): The format of the input keypoints.\n            - \"xy\": [x, y]\n            - \"yx\": [y, x]\n            - \"xya\": [x, y, angle]\n            - \"xys\": [x, y, scale]\n            - \"xyas\": [x, y, angle, scale]\n            - \"xysa\": [x, y, scale, angle]\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        check_validity (bool, optional): If True, check if the converted keypoints are within the image boundaries.\n                                         Defaults to False.\n        angle_in_degrees (bool, optional): If True, convert input angles from degrees to radians.\n                                           Defaults to True.\n\n    Returns:\n        np.ndarray: Array of keypoints in Albumentations format [x, y, angle, scale] with shape (N, 4+).\n                    Any additional columns from the input keypoints are preserved and appended after the\n                    first 4 columns.\n\n    Raises:\n        ValueError: If the source_format is not one of the supported formats.\n\n    Note:\n        - Angles are converted to the range [0, 2\u03c0) radians.\n        - If the input keypoints have additional columns beyond what's specified in the source_format,\n          these columns are preserved in the output.\n    \"\"\"\n    if source_format not in keypoint_formats:\n        raise ValueError(f\"Unknown source_format {source_format}. Supported formats are: {keypoint_formats}\")\n\n    format_to_indices: dict[str, list[int | None]] = {\n        \"xy\": [0, 1, None, None],\n        \"yx\": [1, 0, None, None],\n        \"xya\": [0, 1, 2, None],\n        \"xys\": [0, 1, None, 2],\n        \"xyas\": [0, 1, 2, 3],\n        \"xysa\": [0, 1, 3, 2],\n    }\n\n    indices: list[int | None] = format_to_indices[source_format]\n\n    processed_keypoints = np.zeros((keypoints.shape[0], NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS), dtype=np.float32)\n\n    for i, idx in enumerate(indices):\n        if idx is not None:\n            processed_keypoints[:, i] = keypoints[:, idx]\n\n    if angle_in_degrees and indices[2] is not None:\n        processed_keypoints[:, 2] = np.radians(processed_keypoints[:, 2])\n\n    processed_keypoints[:, 2] = angle_to_2pi_range(processed_keypoints[:, 2])\n\n    if keypoints.shape[1] > len(source_format):\n        processed_keypoints = np.column_stack((processed_keypoints, keypoints[:, len(source_format) :]))\n\n    if check_validity:\n        check_keypoints(processed_keypoints, image_shape)\n\n    return processed_keypoints\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.filter_keypoints","title":"def filter_keypoints (keypoints, image_shape, remove_invisible) [view source on GitHub]","text":"

Filter keypoints to remove those outside the image boundaries.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint (x, y, ...).

image_shape tuple[int, int]

A tuple (height, width) representing the image dimensions.

remove_invisible bool

If True, remove keypoints outside the image boundaries.

Returns:

Type Description np.ndarray

A numpy array of filtered keypoints.

Source code in albumentations/core/keypoints_utils.py Python
def filter_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    remove_invisible: bool,\n) -> np.ndarray:\n    \"\"\"Filter keypoints to remove those outside the image boundaries.\n\n    Args:\n        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.\n                   Each row represents a keypoint (x, y, ...).\n        image_shape: A tuple (height, width) representing the image dimensions.\n        remove_invisible: If True, remove keypoints outside the image boundaries.\n\n    Returns:\n        A numpy array of filtered keypoints.\n    \"\"\"\n    if not remove_invisible:\n        return keypoints\n\n    if not keypoints.size:\n        return keypoints\n\n    height, width = image_shape[:2]\n\n    # Create boolean mask for visible keypoints\n    x, y = keypoints[:, 0], keypoints[:, 1]\n    visible = (x >= 0) & (x < width) & (y >= 0) & (y < height)\n\n    # Apply the mask to filter keypoints\n    return keypoints[visible]\n
"},{"location":"api_reference/core/serialization/","title":"Serialization API (core.serialization)","text":""},{"location":"api_reference/core/serialization/#albumentations.core.serialization.Serializable","title":"class Serializable [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/serialization.py Python
class Serializable(metaclass=SerializableMeta):\n    @classmethod\n    @abstractmethod\n    def is_serializable(cls) -> bool:\n        raise NotImplementedError\n\n    @classmethod\n    @abstractmethod\n    def get_class_fullname(cls) -> str:\n        raise NotImplementedError\n\n    @abstractmethod\n    def to_dict_private(self) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def to_dict(self, on_not_implemented_error: str = \"raise\") -> dict[str, Any]:\n        \"\"\"Take a transform pipeline and convert it to a serializable representation that uses only standard\n        python data types: dictionaries, lists, strings, integers, and floats.\n\n        Args:\n            self: A transform that should be serialized. If the transform doesn't implement the `to_dict`\n                method and `on_not_implemented_error` equals to 'raise' then `NotImplementedError` is raised.\n                If `on_not_implemented_error` equals to 'warn' then `NotImplementedError` will be ignored\n                but no transform parameters will be serialized.\n            on_not_implemented_error (str): `raise` or `warn`.\n\n        \"\"\"\n        if on_not_implemented_error not in {\"raise\", \"warn\"}:\n            msg = f\"Unknown on_not_implemented_error value: {on_not_implemented_error}. Supported values are: 'raise' \"\n            \"and 'warn'\"\n            raise ValueError(msg)\n        try:\n            transform_dict = self.to_dict_private()\n        except NotImplementedError:\n            if on_not_implemented_error == \"raise\":\n                raise\n\n            transform_dict = {}\n            warnings.warn(\n                f\"Got NotImplementedError while trying to serialize {self}. Object arguments are not preserved. \"\n                f\"Implement either '{self.__class__.__name__}.get_transform_init_args_names' \"\n                f\"or '{self.__class__.__name__}.get_transform_init_args' \"\n                \"method to make the transform serializable\",\n                stacklevel=2,\n            )\n        return {\"__version__\": __version__, \"transform\": transform_dict}\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.SerializableMeta","title":"class SerializableMeta [view source on GitHub]","text":"

A metaclass that is used to register classes in SERIALIZABLE_REGISTRY or NON_SERIALIZABLE_REGISTRY so they can be found later while deserializing transformation pipeline using classes full names.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/serialization.py Python
class SerializableMeta(ABCMeta):\n    \"\"\"A metaclass that is used to register classes in `SERIALIZABLE_REGISTRY` or `NON_SERIALIZABLE_REGISTRY`\n    so they can be found later while deserializing transformation pipeline using classes full names.\n    \"\"\"\n\n    def __new__(cls, name: str, bases: tuple[type, ...], *args: Any, **kwargs: Any) -> SerializableMeta:\n        cls_obj = super().__new__(cls, name, bases, *args, **kwargs)\n        if name != \"Serializable\" and ABC not in bases:\n            if cls_obj.is_serializable():\n                SERIALIZABLE_REGISTRY[cls_obj.get_class_fullname()] = cls_obj\n            else:\n                NON_SERIALIZABLE_REGISTRY[cls_obj.get_class_fullname()] = cls_obj\n        return cls_obj\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def _to_dict(cls) -> dict[str, Any]:\n        return {}\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.from_dict","title":"def from_dict (transform_dict, nonserializable=None) [view source on GitHub]","text":"

transform_dict: A dictionary with serialized transform pipeline. nonserializable (dict): A dictionary that contains non-serializable transforms. This dictionary is required when you are restoring a pipeline that contains non-serializable transforms. Keys in that dictionary should be named same as name arguments in respective transforms from a serialized pipeline.

Source code in albumentations/core/serialization.py Python
def from_dict(\n    transform_dict: dict[str, Any],\n    nonserializable: dict[str, Any] | None = None,\n) -> Serializable | None:\n    \"\"\"Args:\n    transform_dict: A dictionary with serialized transform pipeline.\n    nonserializable (dict): A dictionary that contains non-serializable transforms.\n        This dictionary is required when you are restoring a pipeline that contains non-serializable transforms.\n        Keys in that dictionary should be named same as `name` arguments in respective transforms from\n        a serialized pipeline.\n\n    \"\"\"\n    register_additional_transforms()\n    transform = transform_dict[\"transform\"]\n    lmbd = instantiate_nonserializable(transform, nonserializable)\n    if lmbd:\n        return lmbd\n    name = transform[\"__class_fullname__\"]\n    args = {k: v for k, v in transform.items() if k != \"__class_fullname__\"}\n    cls = SERIALIZABLE_REGISTRY[shorten_class_name(name)]\n    if \"transforms\" in args:\n        args[\"transforms\"] = [from_dict({\"transform\": t}, nonserializable=nonserializable) for t in args[\"transforms\"]]\n    return cls(**args)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.get_shortest_class_fullname","title":"def get_shortest_class_fullname (cls) [view source on GitHub]","text":"

The function get_shortest_class_fullname takes a class object as input and returns its shortened full name.

:param cls: The parameter cls is of type Type[BasicCompose], which means it expects a class that is a subclass of BasicCompose :type cls: Type[BasicCompose] :return: a string, which is the shortened version of the full class name.

Source code in albumentations/core/serialization.py Python
def get_shortest_class_fullname(cls: type[Any]) -> str:\n    \"\"\"The function `get_shortest_class_fullname` takes a class object as input and returns its shortened\n    full name.\n\n    :param cls: The parameter `cls` is of type `Type[BasicCompose]`, which means it expects a class that\n    is a subclass of `BasicCompose`\n    :type cls: Type[BasicCompose]\n    :return: a string, which is the shortened version of the full class name.\n    \"\"\"\n    class_fullname = f\"{cls.__module__}.{cls.__name__}\"\n    return shorten_class_name(class_fullname)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.load","title":"def load (filepath_or_buffer, data_format='json', nonserializable=None) [view source on GitHub]","text":"

Load a serialized pipeline from a file or file-like object and construct a transform pipeline.

Parameters:

Name Type Description filepath_or_buffer Union[str, Path, TextIO]

The file path or file-like object to read the serialized data from. If a string is provided, it is interpreted as a path to a file. If a file-like object is provided, the serialized data will be read from it directly.

data_format str

The format of the serialized data. Valid options are 'json' and 'yaml'. Defaults to 'json'.

nonserializable Optional[dict[str, Any]]

A dictionary that contains non-serializable transforms. This dictionary is required when restoring a pipeline that contains non-serializable transforms. Keys in the dictionary should be named the same as the name arguments in respective transforms from the serialized pipeline. Defaults to None.

Returns:

Type Description object

The deserialized transform pipeline.

Exceptions:

Type Description ValueError

If data_format is 'yaml' but PyYAML is not installed.

Source code in albumentations/core/serialization.py Python
def load(\n    filepath_or_buffer: str | Path | TextIO,\n    data_format: str = \"json\",\n    nonserializable: dict[str, Any] | None = None,\n) -> object:\n    \"\"\"Load a serialized pipeline from a file or file-like object and construct a transform pipeline.\n\n    Args:\n        filepath_or_buffer (Union[str, Path, TextIO]): The file path or file-like object to read the serialized\n            data from.\n            If a string is provided, it is interpreted as a path to a file. If a file-like object is provided,\n            the serialized data will be read from it directly.\n        data_format (str): The format of the serialized data. Valid options are 'json' and 'yaml'.\n            Defaults to 'json'.\n        nonserializable (Optional[dict[str, Any]]): A dictionary that contains non-serializable transforms.\n            This dictionary is required when restoring a pipeline that contains non-serializable transforms.\n            Keys in the dictionary should be named the same as the `name` arguments in respective transforms\n            from the serialized pipeline. Defaults to None.\n\n    Returns:\n        object: The deserialized transform pipeline.\n\n    Raises:\n        ValueError: If `data_format` is 'yaml' but PyYAML is not installed.\n\n    \"\"\"\n    check_data_format(data_format)\n\n    if isinstance(filepath_or_buffer, (str, Path)):  # Assume it's a filepath\n        with open(filepath_or_buffer) as f:\n            if data_format == \"json\":\n                transform_dict = json.load(f)\n            else:\n                if not yaml_available:\n                    msg = \"You need to install PyYAML to load a pipeline in yaml format\"\n                    raise ValueError(msg)\n                transform_dict = yaml.safe_load(f)\n    elif data_format == \"json\":\n        transform_dict = json.load(filepath_or_buffer)\n    else:\n        if not yaml_available:\n            msg = \"You need to install PyYAML to load a pipeline in yaml format\"\n            raise ValueError(msg)\n        transform_dict = yaml.safe_load(filepath_or_buffer)\n\n    return from_dict(transform_dict, nonserializable=nonserializable)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.register_additional_transforms","title":"def register_additional_transforms () [view source on GitHub]","text":"

Register transforms that are not imported directly into the albumentations module by checking the availability of optional dependencies.

Source code in albumentations/core/serialization.py Python
def register_additional_transforms() -> None:\n    \"\"\"Register transforms that are not imported directly into the `albumentations` module by checking\n    the availability of optional dependencies.\n    \"\"\"\n    if importlib.util.find_spec(\"torch\") is not None:\n        try:\n            # Import `albumentations.pytorch` only if `torch` is installed.\n            import albumentations.pytorch\n\n            # Use a dummy operation to acknowledge the use of the imported module and avoid linting errors.\n            _ = albumentations.pytorch.ToTensorV2\n        except ImportError:\n            pass\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.save","title":"def save (transform, filepath_or_buffer, data_format='json', on_not_implemented_error='raise') [view source on GitHub]","text":"

Serialize a transform pipeline and save it to either a file specified by a path or a file-like object in either JSON or YAML format.

Parameters:

Name Type Description transform Serializable

The transform pipeline to serialize.

filepath_or_buffer Union[str, Path, TextIO]

The file path or file-like object to write the serialized data to. If a string is provided, it is interpreted as a path to a file. If a file-like object is provided, the serialized data will be written to it directly.

data_format str

The format to serialize the data in. Valid options are 'json' and 'yaml'. Defaults to 'json'.

on_not_implemented_error str

Determines the behavior if a transform does not implement the to_dict method. If set to 'raise', a NotImplementedError is raised. If set to 'warn', the exception is ignored, and no transform arguments are saved. Defaults to 'raise'.

Exceptions:

Type Description ValueError

If data_format is 'yaml' but PyYAML is not installed.

Source code in albumentations/core/serialization.py Python
def save(\n    transform: Serializable,\n    filepath_or_buffer: str | Path | TextIO,\n    data_format: str = \"json\",\n    on_not_implemented_error: str = \"raise\",\n) -> None:\n    \"\"\"Serialize a transform pipeline and save it to either a file specified by a path or a file-like object\n    in either JSON or YAML format.\n\n    Args:\n        transform (Serializable): The transform pipeline to serialize.\n        filepath_or_buffer (Union[str, Path, TextIO]): The file path or file-like object to write the serialized\n            data to.\n            If a string is provided, it is interpreted as a path to a file. If a file-like object is provided,\n            the serialized data will be written to it directly.\n        data_format (str): The format to serialize the data in. Valid options are 'json' and 'yaml'.\n            Defaults to 'json'.\n        on_not_implemented_error (str): Determines the behavior if a transform does not implement the `to_dict` method.\n            If set to 'raise', a `NotImplementedError` is raised. If set to 'warn', the exception is ignored, and\n            no transform arguments are saved. Defaults to 'raise'.\n\n    Raises:\n        ValueError: If `data_format` is 'yaml' but PyYAML is not installed.\n\n    \"\"\"\n    check_data_format(data_format)\n    transform_dict = transform.to_dict(on_not_implemented_error=on_not_implemented_error)\n    transform_dict = serialize_enum(transform_dict)\n\n    # Determine whether to write to a file or a file-like object\n    if isinstance(filepath_or_buffer, (str, Path)):  # It's a filepath\n        with open(filepath_or_buffer, \"w\") as f:\n            if data_format == \"yaml\":\n                if not yaml_available:\n                    msg = \"You need to install PyYAML to save a pipeline in YAML format\"\n                    raise ValueError(msg)\n                yaml.safe_dump(transform_dict, f, default_flow_style=False)\n            elif data_format == \"json\":\n                json.dump(transform_dict, f)\n    elif data_format == \"yaml\":\n        if not yaml_available:\n            msg = \"You need to install PyYAML to save a pipeline in YAML format\"\n            raise ValueError(msg)\n        yaml.safe_dump(transform_dict, filepath_or_buffer, default_flow_style=False)\n    elif data_format == \"json\":\n        json.dump(transform_dict, filepath_or_buffer, indent=2)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.serialize_enum","title":"def serialize_enum (obj) [view source on GitHub]","text":"

Recursively search for Enum objects and convert them to their value. Also handle any Mapping or Sequence types.

Source code in albumentations/core/serialization.py Python
def serialize_enum(obj: Any) -> Any:\n    \"\"\"Recursively search for Enum objects and convert them to their value.\n    Also handle any Mapping or Sequence types.\n    \"\"\"\n    if isinstance(obj, Mapping):\n        return {k: serialize_enum(v) for k, v in obj.items()}\n    if isinstance(obj, Sequence) and not isinstance(obj, str):  # exclude strings since they're also sequences\n        return [serialize_enum(v) for v in obj]\n    return obj.value if isinstance(obj, Enum) else obj\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.to_dict","title":"def to_dict (transform, on_not_implemented_error='raise') [view source on GitHub]","text":"

Take a transform pipeline and convert it to a serializable representation that uses only standard python data types: dictionaries, lists, strings, integers, and floats.

Parameters:

Name Type Description transform Serializable

A transform that should be serialized. If the transform doesn't implement the to_dict method and on_not_implemented_error equals to 'raise' then NotImplementedError is raised. If on_not_implemented_error equals to 'warn' then NotImplementedError will be ignored but no transform parameters will be serialized.

on_not_implemented_error str

raise or warn.

Source code in albumentations/core/serialization.py Python
def to_dict(transform: Serializable, on_not_implemented_error: str = \"raise\") -> dict[str, Any]:\n    \"\"\"Take a transform pipeline and convert it to a serializable representation that uses only standard\n    python data types: dictionaries, lists, strings, integers, and floats.\n\n    Args:\n        transform: A transform that should be serialized. If the transform doesn't implement the `to_dict`\n            method and `on_not_implemented_error` equals to 'raise' then `NotImplementedError` is raised.\n            If `on_not_implemented_error` equals to 'warn' then `NotImplementedError` will be ignored\n            but no transform parameters will be serialized.\n        on_not_implemented_error (str): `raise` or `warn`.\n\n    \"\"\"\n    return transform.to_dict(on_not_implemented_error)\n
"},{"location":"api_reference/core/transforms_interface/","title":"Transforms Interface (core.transforms_interface)","text":""},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.BaseTransformInitSchema","title":"class BaseTransformInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class BaseTransformInitSchema(BaseModel):\n    model_config = ConfigDict(arbitrary_types_allowed=True)\n    always_apply: bool | None\n    p: ProbabilityType\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.BasicTransform","title":"class BasicTransform (p=0.5, always_apply=None) [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class BasicTransform(Serializable, metaclass=CombinedMeta):\n    _targets: tuple[Targets, ...] | Targets  # targets that this transform can work on\n    _available_keys: set[str]  # targets that this transform, as string, lower-cased\n    _key2func: dict[\n        str,\n        Callable[..., Any],\n    ]  # mapping for targets (plus additional targets) and methods for which they depend\n    call_backup = None\n    interpolation: int\n    fill: DropoutFillValue\n    fill_mask: ColorType | None\n    # replay mode params\n    deterministic: bool = False\n    save_key = \"replay\"\n    replay_mode = False\n    applied_in_replay = False\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(self, p: float = 0.5, always_apply: bool | None = None):\n        self.p = p\n        if always_apply is not None:\n            if always_apply:\n                warn(\n                    \"always_apply is deprecated. Use `p=1` if you want to always apply the transform.\"\n                    \" self.p will be set to 1.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n                self.p = 1.0\n            else:\n                warn(\n                    \"always_apply is deprecated.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n        self._additional_targets: dict[str, str] = {}\n        # replay mode params\n        self.params: dict[Any, Any] = {}\n        self._key2func = {}\n        self._set_keys()\n        self.processors: dict[str, BboxProcessor | KeypointsProcessor] = {}\n        self.seed: int | None = None\n        self.random_generator = np.random.default_rng(self.seed)\n        self.py_random = random.Random(self.seed)\n\n    def set_random_state(\n        self,\n        random_generator: np.random.Generator,\n        py_random: random.Random,\n    ) -> None:\n        \"\"\"Set random state directly from generators.\n\n        Args:\n            random_generator: numpy random generator to use\n            py_random: python random generator to use\n        \"\"\"\n        self.random_generator = random_generator\n        self.py_random = py_random\n\n    def set_random_seed(self, seed: int | None) -> None:\n        \"\"\"Set random state from seed.\n\n        Args:\n            seed: Random seed to use\n        \"\"\"\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        d = self.to_dict_private()\n        d[\"id\"] = id(self)\n        return d\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        \"\"\"Returns names of arguments that are used in __init__ method of the transform.\"\"\"\n        msg = (\n            f\"Class {self.get_class_fullname()} is not serializable because the `get_transform_init_args_names` \"\n            \"method is not implemented\"\n        )\n        raise NotImplementedError(msg)\n\n    def set_processors(self, processors: dict[str, BboxProcessor | KeypointsProcessor]) -> None:\n        self.processors = processors\n\n    def get_processor(self, key: str) -> BboxProcessor | KeypointsProcessor | None:\n        return self.processors.get(key)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **kwargs: Any) -> Any:\n        if args:\n            msg = \"You have to pass data to augmentations as named arguments, for example: aug(image=image)\"\n            raise KeyError(msg)\n        if self.replay_mode:\n            if self.applied_in_replay:\n                return self.apply_with_params(self.params, **kwargs)\n            return kwargs\n\n        # Reset params at the start of each call\n        self.params = {}\n\n        if self.should_apply(force_apply=force_apply):\n            params = self.get_params()\n            params = self.update_params_shape(params=params, data=kwargs)\n\n            if self.targets_as_params:  # check if all required targets are in kwargs.\n                missing_keys = set(self.targets_as_params).difference(kwargs.keys())\n                if missing_keys and not (missing_keys == {\"image\"} and \"images\" in kwargs):\n                    msg = f\"{self.__class__.__name__} requires {self.targets_as_params} missing keys: {missing_keys}\"\n                    raise ValueError(msg)\n\n            params_dependent_on_data = self.get_params_dependent_on_data(params=params, data=kwargs)\n            params.update(params_dependent_on_data)\n\n            if self.targets_as_params:  # this block will be removed after removing `get_params_dependent_on_targets`\n                targets_as_params = {k: kwargs.get(k) for k in self.targets_as_params}\n                if missing_keys:  # here we expecting case when missing_keys == {\"image\"} and \"images\" in kwargs\n                    targets_as_params[\"image\"] = kwargs[\"images\"][0]\n                params_dependent_on_targets = self.get_params_dependent_on_targets(targets_as_params)\n                params.update(params_dependent_on_targets)\n\n            # Store the final params\n            self.params = params\n\n            if self.deterministic:\n                kwargs[self.save_key][id(self)] = deepcopy(params)\n            return self.apply_with_params(params, **kwargs)\n\n        return kwargs\n\n    def get_applied_params(self) -> dict[str, Any]:\n        \"\"\"Returns the parameters that were used in the last transform application.\n        Returns empty dict if transform was not applied.\n        \"\"\"\n        return self.params\n\n    def should_apply(self, force_apply: bool = False) -> bool:\n        if self.p <= 0.0:\n            return False\n        if self.p >= 1.0 or force_apply:\n            return True\n        return self.py_random.random() < self.p\n\n    def apply_with_params(self, params: dict[str, Any], *args: Any, **kwargs: Any) -> dict[str, Any]:\n        \"\"\"Apply transforms with parameters.\"\"\"\n        params = self.update_params(params, **kwargs)  # remove after move parameters like interpolation\n        res = {}\n        for key, arg in kwargs.items():\n            if key in self._key2func and arg is not None:\n                target_function = self._key2func[key]\n                res[key] = ensure_contiguous_output(\n                    target_function(ensure_contiguous_output(arg), **params),\n                )\n            else:\n                res[key] = arg\n        return res\n\n    def set_deterministic(self, flag: bool, save_key: str = \"replay\") -> BasicTransform:\n        \"\"\"Set transform to be deterministic.\"\"\"\n        if save_key == \"params\":\n            msg = \"params save_key is reserved\"\n            raise KeyError(msg)\n\n        self.deterministic = flag\n        if self.deterministic and self.targets_as_params:\n            warn(\n                self.get_class_fullname() + \" could work incorrectly in ReplayMode for other input data\"\n                \" because its' params depend on targets.\",\n                stacklevel=2,\n            )\n        self.save_key = save_key\n        return self\n\n    def __repr__(self) -> str:\n        state = self.get_base_init_args()\n        state.update(self.get_transform_init_args())\n        return f\"{self.__class__.__name__}({format_args(state)})\"\n\n    def apply(self, img: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform on image.\"\"\"\n        raise NotImplementedError\n\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform on images.\n\n        Args:\n            images: Input images as numpy array of shape:\n                - (num_images, height, width, channels)\n                - (num_images, height, width) for grayscale\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed images as numpy array in the same format as input\n        \"\"\"\n        # Handle batched numpy array input\n        transformed = np.stack([self.apply(image, **params) for image in images])\n        return np.require(transformed, requirements=[\"C_CONTIGUOUS\"])\n\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform slice by slice to a volume.\n\n        Args:\n            volume: Input volume of shape (depth, height, width) or (depth, height, width, channels)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed volume as numpy array in the same format as input\n        \"\"\"\n        return self.apply_to_images(volume, *args, **params)\n\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to multiple volumes.\"\"\"\n        return np.stack([self.apply_to_volume(vol, *args, **params) for vol in volumes])\n\n    def get_params(self) -> dict[str, Any]:\n        \"\"\"Returns parameters independent of input.\"\"\"\n        return {}\n\n    def update_params_shape(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Updates parameters with input shape.\"\"\"\n        # Extract shape from volume, volumes, image, or images\n        if \"volume\" in data:\n            shape = data[\"volume\"][0].shape  # Take first slice of volume\n        elif \"volumes\" in data:\n            shape = data[\"volumes\"][0][0].shape  # Take first slice of first volume\n        elif \"image\" in data:\n            shape = data[\"image\"].shape\n        else:\n            shape = data[\"images\"][0].shape\n\n        # For volumes/images, shape will be either (H, W) or (H, W, C)\n        params[\"shape\"] = shape\n        return params\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Returns parameters dependent on input.\"\"\"\n        return params\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        # mapping for targets and methods for which they depend\n        # for example:\n        # >>  {\"image\": self.apply}\n        # >>  {\"masks\": self.apply_to_masks}\n        raise NotImplementedError\n\n    def _set_keys(self) -> None:\n        \"\"\"Set _available_keys.\"\"\"\n        if not hasattr(self, \"_targets\"):\n            self._available_keys = set()\n        else:\n            self._available_keys = {\n                target.value.lower()\n                for target in (self._targets if isinstance(self._targets, tuple) else [self._targets])\n            }\n        self._available_keys.update(self.targets.keys())\n        self._key2func = {key: self.targets[key] for key in self._available_keys if key in self.targets}\n\n    @property\n    def available_keys(self) -> set[str]:\n        \"\"\"Returns set of available keys.\"\"\"\n        return self._available_keys\n\n    def update_params(self, params: dict[str, Any], **kwargs: Any) -> dict[str, Any]:\n        \"\"\"Update parameters with transform specific params.\n        This method is deprecated, use:\n        - `get_params` for transform specific params like interpolation and\n        - `update_params_shape` for data like shape.\n        \"\"\"\n        if hasattr(self, \"interpolation\"):\n            params[\"interpolation\"] = self.interpolation\n        if hasattr(self, \"fill\"):\n            params[\"fill\"] = self.fill\n        if hasattr(self, \"fill_mask\"):\n            params[\"fill_mask\"] = self.fill_mask\n\n        # Use update_params_shape to get shape consistently\n        return self.update_params_shape(params, kwargs)\n\n    def add_targets(self, additional_targets: dict[str, str]) -> None:\n        \"\"\"Add targets to transform them the same way as one of existing targets.\n        ex: {'target_image': 'image'}\n        ex: {'obj1_mask': 'mask', 'obj2_mask': 'mask'}\n        by the way you must have at least one object with key 'image'\n\n        Args:\n            additional_targets (dict): keys - new target name, values - old target name. ex: {'image2': 'image'}\n\n        \"\"\"\n        for k, v in additional_targets.items():\n            if k in self._additional_targets and v != self._additional_targets[k]:\n                raise ValueError(\n                    f\"Trying to overwrite existed additional targets. \"\n                    f\"Key={k} Exists={self._additional_targets[k]} New value: {v}\",\n                )\n            if v in self._available_keys:\n                self._additional_targets[k] = v\n                self._key2func[k] = self.targets[v]\n                self._available_keys.add(k)\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        \"\"\"Targets used to get params dependent on targets.\n        This is used to check input has all required targets.\n        \"\"\"\n        return []\n\n    def get_params_dependent_on_targets(self, params: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"This method is deprecated.\n        Use `get_params_dependent_on_data` instead.\n        Returns parameters dependent on targets.\n        Dependent target is defined in `self.targets_as_params`\n        \"\"\"\n        return {}\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    def get_base_init_args(self) -> dict[str, Any]:\n        \"\"\"Returns base init args - p\"\"\"\n        return {\"p\": self.p}\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        \"\"\"Exclude seed from init args during serialization\"\"\"\n        args = {k: getattr(self, k) for k in self.get_transform_init_args_names()}\n        args.pop(\"seed\", None)  # Remove seed from args\n        return args\n\n    def to_dict_private(self) -> dict[str, Any]:\n        state = {\"__class_fullname__\": self.get_class_fullname()}\n        state.update(self.get_base_init_args())\n        state.update(self.get_transform_init_args())\n        return state\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.DualTransform","title":"class DualTransform [view source on GitHub]","text":"

A base class for transformations that should be applied both to an image and its corresponding properties such as masks, bounding boxes, and keypoints. This class ensures that when a transform is applied to an image, all associated entities are transformed accordingly to maintain consistency between the image and its annotations.

Methods

apply(img: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to the image.

img: Input image of shape (H, W, C) or (H, W) for grayscale.\n**params: Additional parameters specific to the transform.\n\nReturns Transformed image of the same shape as input.\n

apply_to_images(images: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple images.

images: Input images of shape (N, H, W, C) or (N, H, W) for grayscale.\n**params: Additional parameters specific to the transform.\n\nReturns Transformed images in the same format as input.\n

apply_to_mask(mask: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a mask.

mask: Input mask of shape (H, W), (H, W, C) for multi-channel masks\n**params: Additional parameters specific to the transform.\n\nReturns Transformed mask in the same format as input.\n

apply_to_masks(masks: np.ndarray, **params: Any) -> np.ndarray | list[np.ndarray]: Apply the transform to multiple masks.

masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n**params: Additional parameters specific to the transform.\nReturns Transformed masks in the same format as input.\n

apply_to_keypoints(keypoints: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to keypoints.

!!! keypoints \"Array of shape (N, 2+) where N is the number of keypoints.\"\n    **params: Additional parameters specific to the transform.\nReturns Transformed keypoints array of shape (N, 2+).\n

apply_to_bboxes(bboxes: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to bounding boxes.

!!! bboxes \"Array of shape (N, 4+) where N is the number of bounding boxes,\"\n        and each row is in the format [x_min, y_min, x_max, y_max].\n**params: Additional parameters specific to the transform.\n\nReturns Transformed bounding boxes array of shape (N, 4+).\n

apply_to_volume(volume: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a volume.

volume: Input volume of shape (D, H, W) or (D, H, W, C).\n**params: Additional parameters specific to the transform.\n\nReturns Transformed volume of the same shape as input.\n

apply_to_volumes(volumes: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple volumes.

volumes: Input volumes of shape (N, D, H, W) or (N, D, H, W, C).\n**params: Additional parameters specific to the transform.\n\nReturns Transformed volumes in the same format as input.\n

apply_to_mask3d(mask: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a 3D mask.

mask: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n**params: Additional parameters specific to the transform.\n\nReturns Transformed 3D mask in the same format as input.\n

apply_to_masks3d(masks: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple 3D masks.

masks: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n**params: Additional parameters specific to the transform.\n\nReturns Transformed 3D masks in the same format as input.\n

Note

  • All apply_* methods should maintain the input shape and format of the data.
  • When applying transforms to masks, ensure that discrete values (e.g., class labels) are preserved.
  • For keypoints and bounding boxes, the transformation should maintain their relative positions with respect to the transformed image.
  • The difference between apply_to_mask and apply_to_masks is mainly in how they handle 3D arrays: apply_to_mask treats a 3D array as a multi-channel mask, while apply_to_masks treats it as multiple single-channel masks.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class DualTransform(BasicTransform):\n    \"\"\"A base class for transformations that should be applied both to an image and its corresponding properties\n    such as masks, bounding boxes, and keypoints. This class ensures that when a transform is applied to an image,\n    all associated entities are transformed accordingly to maintain consistency between the image and its annotations.\n\n    Methods:\n        apply(img: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to the image.\n\n            img: Input image of shape (H, W, C) or (H, W) for grayscale.\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed image of the same shape as input.\n\n        apply_to_images(images: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple images.\n\n            images: Input images of shape (N, H, W, C) or (N, H, W) for grayscale.\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed images in the same format as input.\n\n        apply_to_mask(mask: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a mask.\n\n            mask: Input mask of shape (H, W), (H, W, C) for multi-channel masks\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed mask in the same format as input.\n\n        apply_to_masks(masks: np.ndarray, **params: Any) -> np.ndarray | list[np.ndarray]:\n            Apply the transform to multiple masks.\n\n            masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n            **params: Additional parameters specific to the transform.\n            Returns Transformed masks in the same format as input.\n\n        apply_to_keypoints(keypoints: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to keypoints.\n\n            keypoints: Array of shape (N, 2+) where N is the number of keypoints.\n                **params: Additional parameters specific to the transform.\n            Returns Transformed keypoints array of shape (N, 2+).\n\n        apply_to_bboxes(bboxes: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to bounding boxes.\n\n            bboxes: Array of shape (N, 4+) where N is the number of bounding boxes,\n                    and each row is in the format [x_min, y_min, x_max, y_max].\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed bounding boxes array of shape (N, 4+).\n\n        apply_to_volume(volume: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a volume.\n\n            volume: Input volume of shape (D, H, W) or (D, H, W, C).\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed volume of the same shape as input.\n\n        apply_to_volumes(volumes: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple volumes.\n\n            volumes: Input volumes of shape (N, D, H, W) or (N, D, H, W, C).\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed volumes in the same format as input.\n\n        apply_to_mask3d(mask: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a 3D mask.\n\n            mask: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed 3D mask in the same format as input.\n\n        apply_to_masks3d(masks: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple 3D masks.\n\n            masks: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed 3D masks in the same format as input.\n\n    Note:\n        - All `apply_*` methods should maintain the input shape and format of the data.\n        - When applying transforms to masks, ensure that discrete values (e.g., class labels) are preserved.\n        - For keypoints and bounding boxes, the transformation should maintain their relative positions\n            with respect to the transformed image.\n        - The difference between `apply_to_mask` and `apply_to_masks` is mainly in how they handle 3D arrays:\n            `apply_to_mask` treats a 3D array as a multi-channel mask, while `apply_to_masks` treats it as\n            multiple single-channel masks.\n\n    \"\"\"\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"mask\": self.apply_to_mask,\n            \"masks\": self.apply_to_masks,\n            \"mask3d\": self.apply_to_mask3d,\n            \"masks3d\": self.apply_to_masks3d,\n            \"bboxes\": self.apply_to_bboxes,\n            \"keypoints\": self.apply_to_keypoints,\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n        }\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        msg = f\"Method apply_to_keypoints is not implemented in class {self.__class__.__name__}\"\n        raise NotImplementedError(msg)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        raise NotImplementedError(f\"BBoxes not implemented for {self.__class__.__name__}\")\n\n    def apply_to_mask(self, mask: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(mask, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_masks(self, masks: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to multiple masks.\n\n        Args:\n            masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Array of transformed masks with same shape as input\n        \"\"\"\n        return self.apply(masks, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D mask.\n\n        Args:\n            mask3d: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed 3D mask in the same format as input\n        \"\"\"\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D masks.\n\n        Args:\n            masks3d: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed 3D masks in the same format as input\n        \"\"\"\n        return self.apply_to_mask(masks3d, *args, **params)\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.ImageOnlyTransform","title":"class ImageOnlyTransform [view source on GitHub]","text":"

Transform applied to image only.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class ImageOnlyTransform(BasicTransform):\n    \"\"\"Transform applied to image only.\"\"\"\n\n    _targets = (Targets.IMAGE, Targets.VOLUME)\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n        }\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.NoOp","title":"class NoOp [view source on GitHub]","text":"

Identity transform (does nothing).

Targets

image, mask, bboxes, keypoints, volume, mask3d

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class NoOp(DualTransform):\n    \"\"\"Identity transform (does nothing).\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return keypoints\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return bboxes\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return img\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return mask\n\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> np.ndarray:\n        return volume\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> np.ndarray:\n        return mask3d\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.Transform3D","title":"class Transform3D [view source on GitHub]","text":"

Base class for all 3D transforms.

Transform3D inherits from DualTransform because 3D transforms can be applied to both volumes and masks, similar to how 2D DualTransforms work with images and masks.

Targets

volume: 3D numpy array of shape (D, H, W) or (D, H, W, C) volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C) mask: 3D numpy array of shape (D, H, W) masks: Batch of 3D arrays of shape (N, D, H, W)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class Transform3D(DualTransform):\n    \"\"\"Base class for all 3D transforms.\n\n    Transform3D inherits from DualTransform because 3D transforms can be applied to both\n    volumes and masks, similar to how 2D DualTransforms work with images and masks.\n\n    Targets:\n        volume: 3D numpy array of shape (D, H, W) or (D, H, W, C)\n        volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C)\n        mask: 3D numpy array of shape (D, H, W)\n        masks: Batch of 3D arrays of shape (N, D, H, W)\n    \"\"\"\n\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D volume.\"\"\"\n        raise NotImplementedError\n\n    @batch_transform(\"spatial\", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D volumes.\"\"\"\n        return self.apply_to_volume(volumes, *args, **params)\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D mask.\"\"\"\n        return self.apply_to_volume(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D masks.\"\"\"\n        return self.apply_to_mask3d(masks3d, *args, **params)\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        \"\"\"Define valid targets for 3D transforms.\"\"\"\n        return {\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n            \"mask3d\": self.apply_to_mask3d,\n            \"masks3d\": self.apply_to_masks3d,\n        }\n
"},{"location":"api_reference/pytorch/","title":"Index","text":"
  • Transforms (albumentations.pytorch.transforms)
"},{"location":"api_reference/pytorch/transforms/","title":"Transforms (pytorch.transforms)","text":""},{"location":"api_reference/pytorch/transforms/#albumentations.pytorch.transforms.ToTensor3D","title":"class ToTensor3D (p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert 3D volumes and masks to PyTorch tensors.

This transform is designed for 3D medical imaging data. It converts numpy arrays to PyTorch tensors and ensures consistent channel positioning.

For all inputs (volumes and masks): - Input: (D, H, W, C) or (D, H, W) - depth, height, width, [channels] - Output: (C, D, H, W) - channels first format for PyTorch For single-channel input, adds C=1 dimension

Note

This transform always moves channels to first position as this is the standard PyTorch format. For masks that need to stay in DHWC format, use a different transform or handle the transposition after this transform.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/pytorch/transforms.py Python
class ToTensor3D(BasicTransform):\n    \"\"\"Convert 3D volumes and masks to PyTorch tensors.\n\n    This transform is designed for 3D medical imaging data. It converts numpy arrays\n    to PyTorch tensors and ensures consistent channel positioning.\n\n    For all inputs (volumes and masks):\n        - Input:  (D, H, W, C) or (D, H, W) - depth, height, width, [channels]\n        - Output: (C, D, H, W) - channels first format for PyTorch\n                 For single-channel input, adds C=1 dimension\n\n    Note:\n        This transform always moves channels to first position as this is\n        the standard PyTorch format. For masks that need to stay in DHWC format,\n        use a different transform or handle the transposition after this transform.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    def __init__(self, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p, always_apply=always_apply)\n\n    @property\n    def targets(self) -> dict[str, Any]:\n        return {\n            \"volume\": self.apply_to_volume,\n            \"mask3d\": self.apply_to_mask3d,\n        }\n\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert 3D volume to channels-first tensor.\"\"\"\n        if volume.ndim == NUM_VOLUME_DIMENSIONS:  # D,H,W,C\n            return torch.from_numpy(volume.transpose(3, 0, 1, 2))\n        if volume.ndim == NUM_VOLUME_DIMENSIONS - 1:  # D,H,W\n            return torch.from_numpy(volume[np.newaxis, ...])\n        raise ValueError(f\"Expected 3D or 4D array (D,H,W) or (D,H,W,C), got {volume.ndim}D array\")\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert 3D mask to channels-first tensor.\"\"\"\n        return self.apply_to_volume(mask3d, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/pytorch/transforms/#albumentations.pytorch.transforms.ToTensorV2","title":"class ToTensorV2 (transpose_mask=False, p=1.0, always_apply=None) [view source on GitHub]","text":"

Converts images/masks to PyTorch Tensors, inheriting from BasicTransform. For images: - If input is in HWC format, converts to PyTorch CHW format - If input is in HW format, converts to PyTorch 1HW format (adds channel dimension)

Attributes:

Name Type Description transpose_mask bool

If True, transposes 3D input mask dimensions from [height, width, num_channels] to [num_channels, height, width].

p float

Probability of applying the transform. Default: 1.0.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/pytorch/transforms.py Python
class ToTensorV2(BasicTransform):\n    \"\"\"Converts images/masks to PyTorch Tensors, inheriting from BasicTransform.\n    For images:\n        - If input is in `HWC` format, converts to PyTorch `CHW` format\n        - If input is in `HW` format, converts to PyTorch `1HW` format (adds channel dimension)\n\n    Attributes:\n        transpose_mask (bool): If True, transposes 3D input mask dimensions from `[height, width, num_channels]` to\n            `[num_channels, height, width]`.\n        p (float): Probability of applying the transform. Default: 1.0.\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    def __init__(self, transpose_mask: bool = False, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p, always_apply=always_apply)\n        self.transpose_mask = transpose_mask\n\n    @property\n    def targets(self) -> dict[str, Any]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"mask\": self.apply_to_mask,\n            \"masks\": self.apply_to_masks,\n        }\n\n    def apply(self, img: np.ndarray, **params: Any) -> torch.Tensor:\n        if img.ndim not in {MONO_CHANNEL_DIMENSIONS, NUM_MULTI_CHANNEL_DIMENSIONS}:\n            msg = \"Albumentations only supports images in HW or HWC format\"\n            raise ValueError(msg)\n\n        if img.ndim == MONO_CHANNEL_DIMENSIONS:\n            img = np.expand_dims(img, 2)\n\n        return torch.from_numpy(img.transpose(2, 0, 1))\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> torch.Tensor:\n        if self.transpose_mask and mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n            mask = mask.transpose(2, 0, 1)\n        return torch.from_numpy(mask)\n\n    @overload\n    def apply_to_masks(self, masks: list[np.ndarray], **params: Any) -> list[torch.Tensor]: ...\n\n    @overload\n    def apply_to_masks(self, masks: np.ndarray, **params: Any) -> torch.Tensor: ...\n\n    def apply_to_masks(self, masks: np.ndarray | list[np.ndarray], **params: Any) -> torch.Tensor | list[torch.Tensor]:\n        \"\"\"Convert numpy array or list of numpy array masks to torch tensor(s).\n\n        Args:\n            masks: Numpy array of shape (N, H, W) or (N, H, W, C),\n                or a list of numpy arrays with shape (H, W) or (H, W, C).\n            params: Additional parameters.\n\n        Returns:\n            If transpose_mask is True and input is (N, H, W, C), returns tensor of shape (N, C, H, W).\n            If transpose_mask is True and input is (H, W, C), returns a list of tensors with shape (C, H, W).\n            Otherwise, returns tensors with the same shape as input.\n        \"\"\"\n        if isinstance(masks, list):\n            return [self.apply_to_mask(mask, **params) for mask in masks]\n\n        if self.transpose_mask and masks.ndim == NUM_VOLUME_DIMENSIONS:  # (N, H, W, C)\n            masks = np.transpose(masks, (0, 3, 1, 2))  # -> (N, C, H, W)\n        return torch.from_numpy(masks)\n\n    def apply_to_images(self, images: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert batch of images from (N, H, W, C) to (N, C, H, W).\"\"\"\n        if images.ndim != NUM_VOLUME_DIMENSIONS:  # N,H,W,C\n            raise ValueError(f\"Expected 4D array (N,H,W,C), got {images.ndim}D array\")\n        return torch.from_numpy(images.transpose(0, 3, 1, 2))  # -> (N,C,H,W)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"transpose_mask\",)\n
"},{"location":"autoalbument/","title":"AutoAlbument Overview","text":"

AutoAlbument is an AutoML tool that learns image augmentation policies from data using the Faster AutoAugment algorithm. It relieves the user from manually selecting augmentations and tuning their parameters. AutoAlbument provides a complete ready-to-use configuration for an augmentation pipeline.

AutoAlbument supports image classification and semantic segmentation tasks. The library requires Python 3.6 or higher.

The source code and issue tracker are available at https://github.com/albumentations-team/autoalbument

Table of contents:

  • AutoAlbument introduction and core concepts
  • Installation
  • Benchmarks and a comparison with baseline augmentation strategies
  • How to use AutoAlbument
  • How to use an AutoAlbument Docker image
  • How to use a custom classification or semantic segmentation model
  • Metrics and their meaning
  • Tuning parameters
  • Examples
  • Search algorithms
  • FAQ
"},{"location":"autoalbument/benchmarks/","title":"Benchmarks and a comparison with baseline augmentation strategies","text":"

Here is a comparison between a baseline augmentation strategy and an augmentation policy discovered by AutoAlbument for different classification and semantic segmentation tasks. You can read more about these benchmarks in the autoalbument-benchmarks repository.

"},{"location":"autoalbument/benchmarks/#classification","title":"Classification","text":"Dataset Baseline Top-1 Accuracy AutoAlbument Top-1 Accuracy CIFAR10 91.79 96.02 SVHN 98.31 98.48 ImageNet 73.27 75.17"},{"location":"autoalbument/benchmarks/#semantic-segmentation","title":"Semantic segmentation","text":"Dataset Baseline mIOU AutoAlbument mIOU Pascal VOC 73.34 75.55 Cityscapes 79.47 79.92"},{"location":"autoalbument/custom_model/","title":"How to use a custom classification or semantic segmentation model","text":"

By default AutoAlbument uses pytorch-image-models for classification and segmentation_models.pytorch for semantic segmentation. You can use any model from these packages by providing an appropriate model name.

However, you can also use a custom model with AutoAlbument. To do so, you need to define a Discriminator model. This Discriminator model should have two outputs.

  • The first output should provide a prediction for a classification or semantic segmentation task. For classification, it should output a tensor with a shape [batch_size, num_classes] with logits. For semantic segmentation, it should output a tensor with the shape [batch_size, num_classes, height, width] with logits.

  • The second (auxiliary) output should return a tensor with the shape [batch_size] that contains logits for Discriminator's predictions (whether Discriminator thinks that an image wasn't or was augmented).

To create such a model, you need to subclass the autoalbument.faster_autoaugment.models.BaseDiscriminator class and implement the forward method. This method should take a batch of images, that is, a tensor with the shape [batch_size, num_channels, height, width]. It should return a tuple that contains tensors from the two outputs described above.

As an example, take a look at how default classification and semantic segmentation models are defined in AutoAlbument - https://github.com/albumentations-team/autoalbument/blob/master/autoalbument/faster_autoaugment/models.py or explore an example of a custom model for the CIFAR10 dataset.

Next, you need to specify this custom model in config.yaml, an AutoAlbument config file. AutoAlbument uses the instantiate function from Hydra to instantiate an object. You need to set the _target_ config variable in the classification_model or semantic_segmentation_model section, depending on the task. In this config variable, you need to provide a path to a class with the model. This path should be located inside PYTHONPATH, so Hydra could correctly use it. The simplest way is to define your model in a file such as model.py and place this file in the same directory with dataset.py and search.yaml because this directory is automatically added to PYTHONPATH. Next, you could define _target_ such as _target_: model.MyClassificationModel.

Take a look at the CIFAR10 example config that uses a custom model defined in model.py as a starting point for defining a custom model.

"},{"location":"autoalbument/docker/","title":"How to use an AutoAlbument Docker image","text":"

You can run AutoAlbument from a Docker image. The ghcr.io/albumentations-team/autoalbument:latest Docker image contains the latest release version of AutoAlbument.

You can also use an image that contains a specific version of AutoAlbument. In that case, you need to use the AutoAlbument version as a tag for a Docker image, e.g., the ghcr.io/albumentations-team/autoalbument:0.3.0 image contains AutoAlbument 0.3.0.

The latest AutoAlbument image is based on the pytorch/pytorch:1.7.0-cuda11.0-cudnn8-runtime image.

When you run a Docker container with AutoAlbument, you need to mount a config directory (a directory containing dataset.py and search.yaml files) and other required directories, such as a directory that contains training data.

Here is an example command that runs a Docker container that will search for CIFAR10 augmentation policies.

docker run -it --rm --gpus all --ipc=host -v ~/projects/autoalbument/examples/cifar10:/config -v ~/data:/home/autoalbument/data -u $(id -u ${USER}):$(id -g ${USER}) ghcr.io/albumentations-team/autoalbument:latest

Let's take a look at the arguments:

  • --it. Tell Docker that you run an interactive process. Read more in the Docker documentation.
  • --rm. Automatically clean up a container when it exits. Read more in the Docker documentation.
  • --gpus all. Specify GPUs to use. Read more in the Docker documentation.
  • --ipc=host. Increase shared memory size for PyTorch DataLoader. Read more in the PyTorch documentation.
  • -v ~/projects/autoalbument/examples/cifar10:/config. Mounts the ~/projects/autoalbument/examples/cifar10 directory from the host to the /config directory into the container. This example assumes that you have the AutoAlbument repository in the ~/projects/autoalbument/ directory. Generally speaking, you need to mount a directory containing dataset.py and search.yaml into the /config directory in a container.
  • -v ~/data:/home/autoalbument/data. Mounts the directory ~/data that contains the CIFAR10 dataset into the /home/autoalbument/data directory. You can mount a host directory with a dataset into any container directory, but you need to specify config parameters accordingly. In this example, we mount the directory into /home/autoalbument/data because we set this directory (~/data/cifar10) in the config as a root directory for the dataset. Note that Docker doesn't support tilde expansion for the HOME directory, so we explicitly name HOME directory as /home/autoalbument because autoalbument is a default user inside the container.
  • -u $(id -u ${USER}):$(id -g ${USER}). We use that command to tell Docker to use the host's user ID to run code inside a container. We need this command because AutoAlbument will produce artifacts in the config directory (such as augmentation configs and logs). We need that the host user owns those files (and not root, for example) so you can access them afterward.
  • ghcr.io/albumentations-team/autoalbument:latest is the Docker image's name. latest is a tag for the latest stable release. Alternatively, you can use a tag that specifies an AutoAlbument version, e.g., ghcr.io/albumentations-team/autoalbument:0.3.0.
"},{"location":"autoalbument/faq/","title":"FAQ","text":""},{"location":"autoalbument/faq/#search-takes-a-lot-of-time-how-can-i-speed-it-up","title":"Search takes a lot of time. How can I speed it up?","text":"

Instead of a full training dataset, you can use a reduced version to search for augmentation policies. For example, the authors of Faster AutoAugment used 6000 images from the 120 selected classes to find augmentation policies for ImageNet (while the full dataset for ILSVRC contains 1.2 million images and 1000 classes).

"},{"location":"autoalbument/how_to_use/","title":"How to use AutoAlbument","text":"
  1. You need to create a configuration file with AutoAlbument parameters and a Python file that implements a custom PyTorch Dataset for your data. Next, you need to pass those files to AutoAlbument.
  2. AutoAlbument will use Generative Adversarial Network to discover augmentation policies and then create a file containing those policies.
  3. Finally, you can use Albumentations to load augmentation policies from the file and utilize them in your computer vision pipeline.
"},{"location":"autoalbument/how_to_use/#step-1-create-a-configuration-file-and-a-custom-pytorch-dataset-for-your-data","title":"Step 1. Create a configuration file and a custom PyTorch Dataset for your data","text":""},{"location":"autoalbument/how_to_use/#a-create-a-directory-with-configuration-files","title":"a. Create a directory with configuration files","text":"

Run autoalbument-create --config-dir </path/to/directory> --task <deep_learning_task> --num-classes <num_classes>, e.g. autoalbument-create --config-dir ~/experiments/autoalbument-search-cifar10 --task classification --num-classes 10. - A value for the --config-dir option should contain a path to the directory. AutoAlbument will create this directory and put two files into it: dataset.py and search.yaml (more on them later). - A value for the --task option should contain the name of a deep learning task. Supported values are classification and semantic_segmentation. - A value for the --num-classes option should contain the number of distinct classes in the classification or segmentation dataset.

By default, AutoAlbument creates a search.yaml file that contains only most important configuration parameters. To explore all available parameters you can create a config file that contains them all by providing the --generate-full-config argument, e.g. autoalbument-create --config-dir ~/experiments/autoalbument-search-cifar10 --task classification --num-classes 10 --generate-full-config

"},{"location":"autoalbument/how_to_use/#b-add-implementation-for-__len__-and-__getitem__-methods-in-datasetpy","title":"b. Add implementation for __len__ and __getitem__ methods in dataset.py","text":"

The dataset.py file created at step 1 by autoalbument-create contains stubs for implementing a PyTorch dataset (you can read more about creating custom PyTorch datasets here). You need to add implementation for __len__ and __getitem__ methods (and optionally add the initialization logic if required).

A dataset for a classification task should return an image and a class label. A dataset for a segmentation task should return an image and an associated mask.

"},{"location":"autoalbument/how_to_use/#c-optional-adjust-search-parameters-in-searchyaml","title":"c. [Optional] Adjust search parameters in search.yaml","text":"

You may want to change the parameters that AutoAlbument will use to search for augmentation policies. To do this, you need to edit the search.yaml file created by autoalbument-create at step 1. Each configuration parameter contains a comment that describes the meaning of the setting. Please refer to the \"Tuning the search parameters\" section that includes a description of the most critical parameters.

search.yaml is a Hydra config file. You can use all Hydra features inside it.

"},{"location":"autoalbument/how_to_use/#step-2-use-autoalbument-to-search-for-augmentation-policies","title":"Step 2. Use AutoAlbument to search for augmentation policies.","text":"

To search for augmentation policies, run autoalbument-search --config-dir </path/to/directory>, e.g. autoalbument-search --config-dir ~/experiments/autoalbument-search-cifar10. The value of --config-dir should be the same value that was passed to autoalbument-create at step 1.

autoalbument-search will create a directory with output files (by default the path of the directory will be <config_dir>/outputs/<current_date>/<current_time>, but you can customize it in search.yaml). The policy subdirectory will contain JSON files with policies found at each search phase's epoch.

autoalbument-search is a command wrapped with the @hydra.main decorator from Hydra. You can use all Hydra features when calling this command.

AutoAlbument uses PyTorch to search for augmentation policies. You can speed up the search by using a CUDA-capable GPU.

"},{"location":"autoalbument/how_to_use/#step-3-use-albumentations-to-load-augmentation-policies-and-utilize-them-in-your-training-pipeline","title":"Step 3. Use Albumentations to load augmentation policies and utilize them in your training pipeline.","text":"

AutoAlbument produces a JSON file that contains a configuration for an augmentation pipeline. You can load that JSON file with Albumentations:

Text Only
import albumentations as A\ntransform = A.load(\"/path/to/policy.json\")\n

Then you can use the created augmentation pipeline to augment the input data.

For example, to augment an image for a classification task:

Text Only
transformed = transform(image=image)\ntransformed_image = transformed[\"image\"]\n

To augment an image and a mask for a semantic segmentation task: Text Only

transformed = transform(image=image, mask=mask)\ntransformed_image = transformed[\"image\"]\ntransformed_mask = transformed[\"mask\"]\n

"},{"location":"autoalbument/how_to_use/#additional-resources","title":"Additional resources","text":"
  • You can read more about the most important configuration parameters for AutoAlbument in Tuning the search parameters.

  • To see examples of configuration files and custom PyTorch Datasets, please refer to Examples

  • You can read more about using Albumentations for augmentation in those articles Image augmentation for classification, Mask augmentation for segmentation.

  • Refer to this section of the documentation to get examples of how to use Albumentations with PyTorch and TensorFlow 2.

"},{"location":"autoalbument/installation/","title":"Installation","text":"

AutoAlbument requires Python 3.6 or higher.

"},{"location":"autoalbument/installation/#pypi","title":"PyPI","text":"

To install the latest stable version from PyPI:

pip install -U autoalbument

"},{"location":"autoalbument/installation/#github","title":"GitHub","text":"

To install the latest version from GitHub:

pip install -U git+https://github.com/albumentations-team/autoalbument

"},{"location":"autoalbument/introduction/","title":"AutoAlbument introduction and core concepts","text":""},{"location":"autoalbument/introduction/#what-is-autoalbument","title":"What is AutoAlbument","text":"

AutoAlbument is a tool that automatically searches for the best augmentation policies for your data.

Under the hood, it uses the Faster AutoAugment algorithm. Briefly speaking, the idea is to use a GAN-like architecture in which Generator applies augmentation to some input images, and Discriminator must determine whether an image was or wasn't augmented. This process helps to find augmentation policies that will produce images similar to the original images.

"},{"location":"autoalbument/introduction/#how-to-use-autoalbument","title":"How to use AutoAlbument","text":"

To use AutoAlbument, you need to define two things: a PyTorch Dataset for your data and configuration parameters for AutoAlbument. You can read the detailed instruction in the How to use AutoAlbument article.

Internally AutoAlbument uses PyTorch Lightning for training a GAN and Hydra for handling configuration parameters.

Here are a few things about AutoAlbument and Hydra.

"},{"location":"autoalbument/introduction/#hydra","title":"Hydra","text":"

The main internal configuration file is located at autoalbument/cli/conf/config.yaml

Here is its content:

Text Only
defaults:\n - _version\n - task\n - policy_model: default\n - classification_model: default\n - semantic_segmentation_model: default\n - data: default\n - searcher: default\n - trainer: default\n - optim: default\n - callbacks: default\n - logger: default\n - hydra: default\n - seed\n - search\n

Basically, it includes a bunch of config files with default values. Those config files are split into sets of closely related parameters such as model parameters or optimizer parameters. All default config files are located in their respective directories inside autoalbument/cli/conf

The main config file also includes the search.yaml file, which you will use for overriding default parameters for your specific dataset and task (you can read more about creating the search.yaml file with autoalbument-create in How to use AutoAlbument)

To allow great flexibility, AutoAlbument relies heavily on the instantiate function from Hydra. This function allows to define a path to a Python class in a YAML config (using the _target_ parameter) along with arguments to that class, and Hydra will create an instance of this class with the provided arguments.

As a practice example, if a config contains a definition like this:

Text Only
_target_: autoalbument.faster_autoaugment.models.ClassificationModel\nnum_classes: 10\narchitecture: resnet18\npretrained: False\n

AutoAlbument will translate it approximately to the following call:

Text Only
from autoalbument.faster_autoaugment.models import ClassificationModel\n\nmodel = ClassificationModel(num_classes=10, architecture='resnet18', pretrained=False)\n

By relying on this feature, AutoAlbument allows customizing its behavior without changing the library's internal code.

"},{"location":"autoalbument/introduction/#pytorch-lightning","title":"PyTorch Lightning","text":"

AutoAlbument relies on PyTorch Lightning to train a GAN. In AutoAlbument configs, you can configure PyTorch Lightning by passing the appropriate arguments to Trainer through the trainer config or defining a list of Callbacks through the callbacks config.

"},{"location":"autoalbument/metrics/","title":"Metrics and their meaning","text":"

During the search phase, AutoAlbument outputs four metrics: loss, d_loss, a_loss, and Average Parameter Change (at the end of an epoch).

"},{"location":"autoalbument/metrics/#a_loss","title":"a_loss","text":"

a_loss is a loss for the policy network (or Generator in terms of GAN), which applies augmentations to input images.

"},{"location":"autoalbument/metrics/#d_loss","title":"d_loss","text":"

d_loss is a loss for the Discriminator, the network that tries to guess whether the input image is an augmented or non-augmented one.

"},{"location":"autoalbument/metrics/#loss","title":"loss","text":"

loss is a task-specific loss (CrossEntropyLoss for classification, BCEWithLogitsLoss for semantic segmentation) that acts as a regularizer and prevents the policy network from applying such augmentations that will make an object with class A looks like an object with class B.

"},{"location":"autoalbument/metrics/#average-parameter-change","title":"Average Parameter Change","text":"

Average Parameter Change is a difference between magnitudes of augmentation parameters multiplied by their probabilities at the end of an epoch and the same parameters at the beginning of the epoch. The metric is calculated using the following formula:

  • m' and m are magnitude values for the i-th augmentation at the end and the beginning of the epoch, respectively.
  • p' and p are probability values for the i-th augmentation at the end and the beginning of the epoch, respectively.

The intuition behind this metric is that at the beginning, augmentation parameters are initialized at random, so they are now optimal and prone to heavy change at each epoch. After some time, these parameters should begin to converge, and they should change less at each epoch.

"},{"location":"autoalbument/metrics/#examples-for-metric-values","title":"Examples for metric values","text":"

Below are TensorBoard logs for AutoAlbument on different datasets. The search was performed using AutoAlbument configs from the examples directory.

  • CIFAR10
  • SVHN
  • ImageNet
  • Pascal VOC
  • Cityscapes

As you see, in all these charts, loss is slightly decreasing at each epoch, and a_loss or d_loss could either decrease or increase. Average Parameter Change is usually large at first epochs, but then it starts to decrease. As a rule of thumb, to decide whether you should stop AutoAlbument search and use the resulting policy, you should check that Average Parameter Change is stopped decreasing and started to oscillate, wait for a few more epochs, and use the found policy from that epoch.

In autoalbument-benchmaks, we use AutoAlbument policies produced by the last epoch on these charts.

"},{"location":"autoalbument/search_algorithms/","title":"Search algorithms","text":"

AutoAlbument uses the following algorithms to search for augmentation policies.

"},{"location":"autoalbument/search_algorithms/#faster-autoaugment","title":"Faster AutoAugment","text":"

\"Faster AutoAugment: Learning Augmentation Strategies using Backpropagation\" by Ryuichiro Hataya, Jan Zdenek, Kazuki Yoshizoe, and Hideki Nakayama. Paper | Original implementation

"},{"location":"autoalbument/tuning_parameters/","title":"Tuning the search parameters","text":"

The search.yaml file contains parameters for the search of augmentation policies. Here is an example search.yaml for image classification on the CIFAR-10 dataset, and here is an example search.yaml for semantic segmentation on the Pascal VOC dataset.

"},{"location":"autoalbument/tuning_parameters/#task-specific-model","title":"Task-specific model","text":"

A task-specific model is a model that classifies images for a classification task or outputs masks for a semantic segmentation task. Settings for a task-specific model are defined by either classification_model or semantic_segmentation_model depending on a selected task. Ideally, you should select the same model (the same architecture and the same pretrained weights) that you will use in an actual task. AutoAlbument uses models from PyTorch Image Models and Segmentation models packages for classification and semantic segmentation respectively.

"},{"location":"autoalbument/tuning_parameters/#base-pytorch-parameters","title":"Base PyTorch parameters.","text":"

You may want to adjust the following parameters for a PyTorch pipeline:

  • data.dataloader parameters such as batch_size and num_workers
  • Number of epochs to search for best augmentation policies in optim.epochs.
  • Learning rate for optimizers in optim.main.lr and optim.policy.lr.
"},{"location":"autoalbument/tuning_parameters/#parameters-for-the-augmentations-search","title":"Parameters for the augmentations search.","text":"

Those parameters are defined in policy_model. You may want to tune the following ones:

  • num_sub_policies - number of distinct augmentation sub-policies. A random sub-policy is selected in each iteration, and that sub-policy is applied to input data. The larger number of sub-policies will produce a more diverse set of augmentations. On the other side, the more sub-policies you have, the more time and data you need to tune those sub-policies correctly.

  • num_chunks controls the balance between speed and diversity of augmentations in a search phase. Each batch is split-up into num_chunks chunks, and then a random sub-policy is applied to each chunk separately. The larger the value of num_chunks helps to learn augmentation policies better but simultaneously increases the searching time. Authors of FasterAutoAugment used such values for num_chunks that each chunk consisted of 8 to 16 images.

  • operation_count - the number of augmentation operations that will be applied to each input data instance. For example, operation_count: 1 means that only one operation will be applied to an input image/mask, and operation_count: 4 means that four sequential operations will be applied to each input image/mask. The larger number of operations produces a more diverse set of augmentations but simultaneously increases the searching time.

"},{"location":"autoalbument/tuning_parameters/#preprocessing-transforms","title":"Preprocessing transforms","text":"

If images have different sizes or you want to train a model on image patches, you could define preprocessing transforms (such as Resizing, Cropping, and Padding) in data.preprocessing. Those transforms will always be applied to all input data. Found augmentation policies will also contain those preprocessing transforms.

Note that it is crucial for Policy Model (a model that searches for augmentation parameters) to receive images of the same size that will be used during the training of an actual model. For some augmentations, parameters depend on input data's height and width (for example, hole sizes for the Cutout augmentation).

"},{"location":"autoalbument/examples/cifar10/","title":"Image classification on the CIFAR10 dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/cifar10

"},{"location":"autoalbument/examples/cifar10/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/cifar10/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/cifar10/#modelpy","title":"model.py","text":"Python"},{"location":"autoalbument/examples/cityscapes/","title":"Semantic segmentation on Cityscapes dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/cityscapes

"},{"location":"autoalbument/examples/cityscapes/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/cityscapes/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/imagenet/","title":"Image classification on the ImageNet dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/imagenet

"},{"location":"autoalbument/examples/imagenet/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/imagenet/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/list/","title":"List of examples","text":"
  • Image classification on the CIFAR10 dataset.
  • Image classification on the SVHN dataset.
  • Image classification on the ImageNet dataset.
  • Semantic segmentation on the Pascal VOC dataset.
  • Semantic segmentation on the Cityscapes dataset.

To run the search with an example config:

Bash
autoalbument-search --config-dir </path/to/directory_with_dataset.py_and_search.yaml>\n
"},{"location":"autoalbument/examples/pascal_voc/","title":"Semantic segmentation on the Pascal VOC dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/pascal_voc

"},{"location":"autoalbument/examples/pascal_voc/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/pascal_voc/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/svhn/","title":"Image classification on the SVHN dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/svhn

"},{"location":"autoalbument/examples/svhn/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/svhn/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/svhn/#modelpy","title":"model.py","text":"Python"},{"location":"contributing/coding_guidelines/","title":"Coding Guidelines","text":"

This document outlines the coding standards and best practices for contributing to Albumentations.

"},{"location":"contributing/coding_guidelines/#important-note-about-guidelines","title":"Important Note About Guidelines","text":"

These guidelines represent our current best practices, developed through experience maintaining and expanding the Albumentations codebase. While some existing code may not strictly follow these standards (due to historical reasons), we are gradually refactoring the codebase to align with these guidelines.

For new contributions:

  • All new code must follow these guidelines
  • All modifications to existing code should move it closer to these standards
  • Pull requests that introduce patterns we're trying to move away from will not be accepted

For existing code:

  • You may encounter patterns that don't match these guidelines (e.g., transforms with \"Random\" prefix or Union types for parameters)
  • These are considered technical debt that we're working to address
  • When modifying existing code, take the opportunity to align it with current standards where possible
"},{"location":"contributing/coding_guidelines/#code-style-and-formatting","title":"Code Style and Formatting","text":""},{"location":"contributing/coding_guidelines/#pre-commit-hooks","title":"Pre-commit Hooks","text":"

We use pre-commit hooks to maintain consistent code quality. These hooks automatically check and format your code before each commit.

  • Install pre-commit if you haven't already:
Bash
pip install pre-commit\npre-commit install\n
  • The hooks will run automatically on git commit. To run manually:
Bash
pre-commit run --files $(find albumentations -type f)\n
"},{"location":"contributing/coding_guidelines/#python-version-and-type-hints","title":"Python Version and Type Hints","text":"
  • Use Python 3.9+ features and syntax
  • Always include type hints using Python 3.10+ typing syntax:
Python
# Correct\ndef transform(self, value: float, range: tuple[float, float]) -> float:\n\n# Incorrect - don't use capital-case types\ndef transform(self, value: float, range: Tuple[float, float]) -> Float:\n
  • Use | instead of Union and for optional types:
Python
# Correct\ndef process(value: int | float | None) -> str:\n\n# Incorrect\ndef process(value: Optional[Union[int, float]) -> str:\n
"},{"location":"contributing/coding_guidelines/#naming-conventions","title":"Naming Conventions","text":""},{"location":"contributing/coding_guidelines/#transform-names","title":"Transform Names","text":"
  • Avoid adding \"Random\" prefix to new transforms
Python
# Correct\nclass Brightness(ImageOnlyTransform):\n\n# Incorrect (historical pattern)\nclass RandomBrightness(ImageOnlyTransform):\n
"},{"location":"contributing/coding_guidelines/#parameter-naming","title":"Parameter Naming","text":"
  • Use _range suffix for interval parameters:
Python
# Correct\nbrightness_range: tuple[float, float]\nshadow_intensity_range: tuple[float, float]\n\n# Incorrect\nbrightness_limit: tuple[float, float]\nshadow_intensity: tuple[float, float]\n
"},{"location":"contributing/coding_guidelines/#standard-parameter-names","title":"Standard Parameter Names","text":"

For transforms that handle gaps or boundaries, use these consistent names:

  • border_mode: Specifies how to handle gaps, not mode or pad_mode
  • fill: Defines how to fill holes (pixel value or method), not fill_value, cval, fill_color, pad_value, pad_cval, value, color
  • fill_mask: Same as fill but for mask filling, not fill_mask_value, fill_mask_color, fill_mask_cval
"},{"location":"contributing/coding_guidelines/#parameter-types-and-ranges","title":"Parameter Types and Ranges","text":""},{"location":"contributing/coding_guidelines/#parameter-definitions","title":"Parameter Definitions","text":"
  • Prefer range parameters over fixed values:
Python
# Correct\ndef __init__(self, brightness_range: tuple[float, float] = (-0.2, 0.2)):\n\n# Avoid\ndef __init__(self, brightness: float = 0.2):\n
"},{"location":"contributing/coding_guidelines/#avoid-union-types-for-parameters","title":"Avoid Union Types for Parameters","text":"
  • Don't use Union[float, tuple[float, float]] for parameters
  • Instead, always use ranges where sampling is needed:
Python
# Correct\nscale_range: tuple[float, float] = (0.5, 1.5)\n\n# Avoid\nscale: float | tuple[float, float] = 1.0\n
  • For fixed values, use same value for both range ends:
Python
brightness_range = (0.1, 0.1)  # Fixed brightness of 0.1\n
"},{"location":"contributing/coding_guidelines/#transform-design-principles","title":"Transform Design Principles","text":""},{"location":"contributing/coding_guidelines/#relative-parameters","title":"Relative Parameters","text":"
  • Prefer parameters that are relative to image dimensions rather than fixed pixel values:
Python
# Correct - relative to image size\ndef __init__(self, crop_size_range: tuple[float, float] = (0.1, 0.3)):\n    # crop_size will be fraction of min(height, width)\n\n# Avoid - fixed pixel values\ndef __init__(self, crop_size_range: tuple[int, int] = (32, 96)):\n    # crop_size will be fixed regardless of image size\n
"},{"location":"contributing/coding_guidelines/#data-type-consistency","title":"Data Type Consistency","text":"
  • Ensure transforms produce consistent results regardless of input data type
  • Use provided decorators to handle type conversions:
  • @uint8_io: For transforms that work with uint8 images
  • @float32_io: For transforms that work with float32 images

The decorators will:

  • Pass through images that are already in the target type without conversion
  • Convert other types as needed and convert back after processing
Python
@uint8_io  # If input is uint8 => use as is; if float32 => convert to uint8, process, convert back\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    # img is guaranteed to be uint8\n    # if input was float32 => result will be converted back to float32\n    # if input was uint8 => result will stay uint8\n    return cv2.blur(img, (3, 3))\n\n@float32_io  # If input is float32 => use as is; if uint8 => convert to float32, process, convert back\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    # img is guaranteed to be float32 in range [0, 1]\n    # if input was uint8 => result will be converted back to uint8\n    # if input was float32 => result will stay float32\n    return img * 0.5\n\n# Avoid - manual type conversion\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    if img.dtype != np.uint8:\n        img = (img * 255).clip(0, 255).astype(np.uint8)\n    result = cv2.blur(img, (3, 3))\n    if img.dtype != np.uint8:\n        result = result.astype(np.float32) / 255\n    return result\n
"},{"location":"contributing/coding_guidelines/#channel-flexibility","title":"Channel Flexibility","text":"
  • Support arbitrary number of channels unless specifically constrained:

```python # Correct - works with any number of channels def apply(self, img: np.ndarray, **params) -> np.ndarray: # img shape is (H, W, C), works for any C return img * self.factor

# Also correct - explicitly requires RGB def apply(self, img: np.ndarray, **params) -> np.ndarray: if img.shape[-1] != 3: raise ValueError(\"Transform requires RGB image\") return rgb_to_hsv(img) # RGB-specific processing

"},{"location":"contributing/coding_guidelines/#random-number-generation","title":"Random Number Generation","text":""},{"location":"contributing/coding_guidelines/#using-random-generators","title":"Using Random Generators","text":"
  • Use class-level random generators instead of direct numpy or random calls:
Python
# Correct\nvalue = self.random_generator.uniform(0, 1, size=image.shape)\nchoice = self.py_random.choice(options)\n\n# Incorrect\nvalue = np.random.uniform(0, 1, size=image.shape)\nchoice = random.choice(options)\n
  • Prefer Python's standard library random over numpy.random:
Python
# Correct - using standard library random (faster)\nvalue = self.py_random.uniform(0, 1)\nchoice = self.py_random.choice(options)\n\n# Use numpy.random only when needed\nvalue = self.random_generator.randint(0, 255, size=image.shape)\n
"},{"location":"contributing/coding_guidelines/#parameter-sampling","title":"Parameter Sampling","text":"
  • Handle all probability calculations in get_params or get_params_dependent_on_data
  • Don't perform random operations in apply_xxx or __init__ methods:
Python
def get_params(self):\n    return {\n        \"brightness\": self.random_generator.uniform(\n            self.brightness_range[0],\n            self.brightness_range[1]\n        )\n    }\n
"},{"location":"contributing/coding_guidelines/#transform-development","title":"Transform Development","text":""},{"location":"contributing/coding_guidelines/#method-definitions","title":"Method Definitions","text":"
  • Don't use default arguments in apply_xxx methods:
Python
# Correct\ndef apply_to_mask(self, mask: np.ndarray, fill_mask: int) -> np.ndarray:\n\n# Incorrect\ndef apply_to_mask(self, mask: np.ndarray, fill_mask: int = 0) -> np.ndarray:\n
"},{"location":"contributing/coding_guidelines/#parameter-generation","title":"Parameter Generation","text":""},{"location":"contributing/coding_guidelines/#using-get_params_dependent_on_data","title":"Using get_params_dependent_on_data","text":"

This method provides access to image shape and target data for parameter generation:

Python
def get_params_dependent_on_data(\n    self,\n    params: dict[str, Any],\n    data: dict[str, Any]\n) -> dict[str, Any]:\n    # Access image shape - always available\n    height, width = params[\"shape\"][:2]\n\n    # Access targets if they were passed to transform\n    image = data.get(\"image\")  # Original image\n    mask = data.get(\"mask\")    # Segmentation mask\n    bboxes = data.get(\"bboxes\")  # Bounding boxes\n    keypoints = data.get(\"keypoints\")  # Keypoint coordinates\n\n    # Example: Calculate parameters based on image size\n    crop_size = min(height, width) // 2\n    center_x = width // 2\n    center_y = height // 2\n\n    return {\n        \"crop_size\": crop_size,\n        \"center\": (center_x, center_y)\n    }\n

The method receives:

  • params: Dictionary containing image metadata, where params[\"shape\"] is always available
  • data: Dictionary containing all targets passed to the transform

Use this method when you need to:

  • Calculate parameters based on image dimensions
  • Access target data for parameter generation
  • Ensure transform parameters are appropriate for the input data
"},{"location":"contributing/coding_guidelines/#parameter-validation-with-initschema","title":"Parameter Validation with InitSchema","text":"

Each transform must include an InitSchema class that inherits from BaseTransformInitSchema. This class is responsible for:

  • Validating input parameters before __init__ execution
  • Converting parameter types if needed
  • Ensuring consistent parameter handling
Python
# Correct - full parameter validation\nclass RandomGravel(ImageOnlyTransform):\n    class InitSchema(BaseTransformInitSchema):\n      slant_range: Annotated[tuple[float, float], AfterValidator(nondecreasing)]\n      brightness_coefficient: float = Field(gt=0, le=1)\n\n\n  def __init__(self, slant_range: tuple[float, float], brightness_coefficient: float, p: float = 0.5):\n      super().__init__(p=p)\n      self.slant_range = slant_range\n      self.brightness_coefficient = brightness_coefficient\n
Python
# Incorrect - missing InitSchema\nclass RandomGravel(ImageOnlyTransform):\n    def __init__(self, slant_range: tuple[float, float], brightness_coefficient: float, p: float = 0.5):\n        super().__init__(p=p)\n        self.slant_range = slant_range\n        self.brightness_coefficient = brightness_coefficient\n
"},{"location":"contributing/coding_guidelines/#coordinate-systems","title":"Coordinate Systems","text":""},{"location":"contributing/coding_guidelines/#image-center-calculations","title":"Image Center Calculations","text":"

The center point calculation differs slightly between targets:

  • For images, masks, and keypoints:
Python
# Correct - using helper function\nfrom albumentations.augmentations.geometric.functional import center\ncenter_x, center_y = center(image_shape)  # Returns ((width-1)/2, (height-1)/2)\n\n# Incorrect - manual calculation might miss the -1\ncenter_x = width / 2  # Wrong!\ncenter_y = height / 2  # Wrong!\n
  • For bounding boxes:
Python
# Correct - using helper function\nfrom albumentations.augmentations.geometric.functional import center_bbox\ncenter_x, center_y = center_bbox(image_shape)  # Returns (width/2, height/2)\n\n# Incorrect - using wrong center calculation\ncenter_x, center_y = center(image_shape)  # Wrong for bboxes!\n

This small difference is crucial for pixel-perfect accuracy. Always use the appropriate helper functions:

  • center() for image, mask, and keypoint transformations
  • center_bbox() for bounding box transformations
"},{"location":"contributing/coding_guidelines/#serialization-compatibility","title":"Serialization Compatibility","text":"
  • Ensure transforms work with both tuples and lists for range parameters
  • Test serialization/deserialization with JSON and YAML formats
"},{"location":"contributing/coding_guidelines/#documentation","title":"Documentation","text":""},{"location":"contributing/coding_guidelines/#docstrings","title":"Docstrings","text":"
  • Use Google-style docstrings
  • Include type information, parameter descriptions, and examples:
Python
def transform(self, image: np.ndarray) -> np.ndarray:\n    \"\"\"Apply brightness transformation to the image.\n\n    Args:\n        image: Input image in RGB format.\n\n    Returns:\n        Transformed image.\n\n    Examples:\n        >>> transform = Brightness(brightness_range=(-0.2, 0.2))\n        >>> transformed = transform(image=image)\n    \"\"\"\n
"},{"location":"contributing/coding_guidelines/#comments","title":"Comments","text":"
  • Add comments for complex logic
  • Explain why, not what (the code shows what)
  • Keep comments up to date with code changes
"},{"location":"contributing/coding_guidelines/#updating-transform-documentation","title":"Updating Transform Documentation","text":"

When adding a new transform or modifying the targets of an existing one, you must update the transforms documentation in the README:

  1. Generate the updated documentation by running:
Bash
python -m tools.make_transforms_docs make\n
  1. This will output a formatted list of all transforms and their supported targets

  2. Update the relevant section in README.md with the new information

  3. Ensure the documentation accurately reflects which targets (image, mask, bboxes, keypoints, etc.) are supported by each transform

This helps maintain accurate and up-to-date documentation about transform capabilities.

"},{"location":"contributing/coding_guidelines/#testing","title":"Testing","text":""},{"location":"contributing/coding_guidelines/#test-coverage","title":"Test Coverage","text":"
  • Write tests for all new functionality
  • Include edge cases and error conditions
  • Ensure reproducibility with fixed random seeds
"},{"location":"contributing/coding_guidelines/#test-organization","title":"Test Organization","text":"
  • Place tests in the appropriate module under tests/
  • Follow existing test patterns and naming conventions
  • Use pytest fixtures when appropriate
"},{"location":"contributing/coding_guidelines/#code-review-guidelines","title":"Code Review Guidelines","text":"

Before submitting your PR:

  1. Run all tests
  2. Run pre-commit hooks
  3. Check type hints
  4. Update documentation if needed
  5. Ensure code follows these guidelines
"},{"location":"contributing/coding_guidelines/#getting-help","title":"Getting Help","text":"

If you have questions about these guidelines:

  1. Join our Discord community
  2. Open a GitHub issue
  3. Ask in your pull request
"},{"location":"contributing/environment_setup/","title":"Setting Up Your Development Environment","text":"

This guide will help you set up your development environment for contributing to Albumentations.

"},{"location":"contributing/environment_setup/#prerequisites","title":"Prerequisites","text":"
  • Python 3.9 or higher
  • Git
  • A GitHub account
"},{"location":"contributing/environment_setup/#step-by-step-setup","title":"Step-by-Step Setup","text":""},{"location":"contributing/environment_setup/#1-fork-and-clone-the-repository","title":"1. Fork and Clone the Repository","text":"
  1. Fork the Albumentations repository on GitHub
  2. Clone your fork locally:
Bash
git clone https://github.com/YOUR_USERNAME/albumentations.git\ncd albumentations\n
"},{"location":"contributing/environment_setup/#2-create-a-virtual-environment","title":"2. Create a Virtual Environment","text":"

Choose the appropriate commands for your operating system:

"},{"location":"contributing/environment_setup/#linux-macos","title":"Linux / macOS","text":"Bash
python3 -m venv env\nsource env/bin/activate\n
"},{"location":"contributing/environment_setup/#windows-cmdexe","title":"Windows (cmd.exe)","text":"Bash
python -m venv env\nenv\\Scripts\\activate.bat\n
"},{"location":"contributing/environment_setup/#windows-powershell","title":"Windows (PowerShell)","text":"Bash
python -m venv env\nenv\\Scripts\\activate.ps1\n
"},{"location":"contributing/environment_setup/#3-install-dependencies","title":"3. Install Dependencies","text":"
  1. Install the project in editable mode:
Bash
pip install -e .\n
  1. Install development dependencies:
Bash
pip install -r requirements-dev.txt\n
"},{"location":"contributing/environment_setup/#4-set-up-pre-commit-hooks","title":"4. Set Up Pre-commit Hooks","text":"

Pre-commit hooks help maintain code quality by automatically checking your changes before each commit.

  1. Install pre-commit:
Bash
pip install pre-commit\n
  1. Set up the hooks:
Bash
pre-commit install\n
  1. (Optional) Run hooks manually on all files:
Bash
pre-commit run --files $(find albumentations -type f)\n
"},{"location":"contributing/environment_setup/#verifying-your-setup","title":"Verifying Your Setup","text":""},{"location":"contributing/environment_setup/#run-tests","title":"Run Tests","text":"

Ensure everything is set up correctly by running the test suite:

Bash
pytest\n
"},{"location":"contributing/environment_setup/#common-issues-and-solutions","title":"Common Issues and Solutions","text":""},{"location":"contributing/environment_setup/#permission-errors","title":"Permission Errors","text":"
  • Linux/macOS: If you encounter permission errors, try using sudo for system-wide installations or consider using --user flag with pip
  • Windows: Run your terminal as administrator if you encounter permission issues
"},{"location":"contributing/environment_setup/#virtual-environment-not-activating","title":"Virtual Environment Not Activating","text":"
  • Ensure you're in the correct directory
  • Check that Python is properly installed and in your system PATH
  • Try creating the virtual environment with the full Python path
"},{"location":"contributing/environment_setup/#import-errors-after-installation","title":"Import Errors After Installation","text":"
  • Verify that you're using the correct virtual environment
  • Confirm that all dependencies were installed successfully
  • Try reinstalling the package in editable mode
"},{"location":"contributing/environment_setup/#next-steps","title":"Next Steps","text":"

After setting up your environment:

  1. Create a new branch for your work
  2. Make your changes
  3. Run tests and pre-commit hooks
  4. Submit a pull request

For more detailed information about contributing, please refer to Coding Guidelines

"},{"location":"contributing/environment_setup/#getting-help","title":"Getting Help","text":"

If you encounter any issues with the setup:

  1. Check our Discord community
  2. Open an issue on GitHub
  3. Review existing issues for similar problems and solutions
"},{"location":"examples/","title":"List of examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Using Albumentations for a semantic segmentation task
  • Using Albumentations to augment keypoints
  • Applying the same augmentation with the same parameters to multiple images, masks, bounding boxes, or keypoints
  • Weather augmentations in Albumentations
  • Example of applying XYMasking transform
  • Example of applying ChromaticAberration transform
  • Example of applying Morphological transform
  • Example of applying D4 transform
  • Example of applying RandomGridShuffle transform
  • Example of applying OverlayElements transform
  • Example of applying TextImage transform
  • Migrating from torchvision to Albumentations
  • Debugging an augmentation pipeline with ReplayCompose
  • How to save and load parameters of an augmentation pipeline
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
  • How to save and load transforms to HuggingFace Hub.
"},{"location":"examples/#examples-of-how-to-use-albumentations-with-different-deep-learning-frameworks","title":"Examples of how to use Albumentations with different deep learning frameworks","text":"
  • PyTorch
  • PyTorch and Albumentations for image classification
  • PyTorch and Albumentations for semantic segmentation
  • TensorFlow 2
  • Using Albumentations with Tensorflow
"},{"location":"external_resources/blog_posts_podcasts_talks/","title":"Blog posts, podcasts, talks, and videos about Albumentations","text":""},{"location":"external_resources/blog_posts_podcasts_talks/#blog-posts","title":"Blog posts","text":"
  • Custom Image Augmentation with Keras. Solving CIFAR-10 with Albumentations and TPU on Google Colab..
  • Road detection using segmentation models and albumentations libraries on Keras.
  • Image Data Augmentation for TensorFlow 2, Keras and PyTorch with Albumentations in Python
  • Explore image augmentations using a convenient tool
  • Image Augmentation using PyTorch and Albumentations
  • Employing the albumentation library in PyTorch workflows. Bonus: Helper for selecting appropriate values!
  • Overview of Albumentations: Open-source library for advanced image augmentations
"},{"location":"external_resources/blog_posts_podcasts_talks/#podcasts-talks-and-videos","title":"Podcasts, talks, and videos","text":"
  • PyConBY 2020: Eugene Khvedchenya - Albumentations: Fast and Flexible image augmentations
  • Albumentations Framework: a fast image augmentations library | Interview with Dr. Vladimir Iglovikov
  • Image Data Augmentation for TensorFlow 2, Keras and PyTorch with Albumentations in Python
  • Bengali.AI competition - Ch 5. Image augmentations using albumentations
  • Albumentations Tutorial for Data Augmentation
"},{"location":"external_resources/books/","title":"Books that mention Albumentations","text":"
  • Deep Learning For Dummies. John Paul Mueller, Luca Massaron. May 2019.
  • Data Science Programming All-in-One For Dummies. John Paul Mueller, Luca Massaron. January 2020.
  • PyTorch Computer Vision Cookbook. Michael Avendi. March 2020.
  • Approaching (Almost) Any Machine Learning Problem. Abhishek Thakur. June 2020.
"},{"location":"external_resources/online_courses/","title":"Online classes that cover Albumentations","text":""},{"location":"external_resources/online_courses/#udemy","title":"Udemy","text":"
  • Modern Computer Vision & Deep Learning with Python & PyTorch
  • Deep Learning for Image Segmentation with Python & Pytorch
  • Deep Learning Masterclass with TensorFlow 2 Over 20 Projects
  • Master Deep Learning for Computer Vision in TensorFlow
  • Deep Learning : Image Classification with Tensorflow in 2024
  • Deep learning with PyTorch | Medical Imaging Competitions
  • Veri Art\u0131r\u0131m\u0131: Albumentations ile Projelerle Veri Art\u0131r\u0131m\u0131
  • Mastering Advanced Representation Learning (CV)
"},{"location":"external_resources/online_courses/#coursera","title":"Coursera","text":"
  • Deep Learning with PyTorch : Image Segmentation
  • Facial Keypoint Detection with PyTorch
  • Deep Learning with PyTorch : Object Localization
  • Aerial Image Segmentation with PyTorch
"},{"location":"getting_started/augmentation_mapping/","title":"Transform Library Comparison Guide","text":"

This guide helps you find equivalent transforms between Albumentations and other popular libraries (torchvision and Kornia).

"},{"location":"getting_started/augmentation_mapping/#key-differences","title":"Key Differences","text":""},{"location":"getting_started/augmentation_mapping/#compared-to-torchvision","title":"Compared to TorchVision","text":"
  • Albumentations operates on numpy arrays (TorchVision uses PyTorch tensors)
  • More parameters for fine-tuning transformations
  • Built-in support for mask augmentation
  • Better handling of bounding boxes and keypoints
"},{"location":"getting_started/augmentation_mapping/#compared-to-kornia","title":"Compared to Kornia","text":"
  • CPU-based numpy operations (Kornia uses GPU tensors)
  • More comprehensive support for detection/segmentation
  • Generally better CPU performance
  • Simpler API for common tasks
"},{"location":"getting_started/augmentation_mapping/#common-transform-mappings","title":"Common Transform Mappings","text":""},{"location":"getting_started/augmentation_mapping/#basic-geometric-transforms","title":"Basic Geometric Transforms","text":"TorchVision Transform Albumentations Equivalent Notes Resize Resize / LongestMaxSize - TorchVision's Resize combines two Albumentations behaviors:\u00a0\u00a01. When given (h,w): equivalent to Albumentations Resize\u00a0\u00a02. When given single int + max_size: similar to LongestMaxSize- Albumentations allows separate interpolation method for masks- TorchVision has antialias parameter, Albumentations doesn't ScaleJitter OneOf + multiple Resize - Can be approximated in Albumentations using OneOf container with multiple Resize transforms- Example: transforms = A.OneOf([ A.Resize(height=int(target_h * scale), width=int(target_w * scale)) for scale in np.linspace(0.1, 2.0, num=20) ])- Not exactly the same as continuous random scaling, but provides similar functionality RandomShortestSize OneOf + SmallestMaxSize - Can be approximated in Albumentations using: transforms = A.OneOf([ A.SmallestMaxSize(max_size=size, max_height=max_size, max_width=max_size) for size in [480, 512, 544, 576, 608] ])- Randomly selects size for shortest side while maintaining aspect ratio- Optional max_size parameter limits longest side- TorchVision has antialias parameter, Albumentations doesn't RandomResize OneOf + Resize - TorchVision: randomly selects single size S between min_size and max_size, sets both width and height to S- No direct equivalent in Albumentations (RandomScale preserves aspect ratio)- Can be approximated using: transforms = A.OneOf([ A.Resize(size, size) for size in range(min_size, max_size + 1, step) ]) RandomCrop RandomCrop - Both perform random cropping with similar core functionality- Key differences:\u00a0\u00a01. TorchVision accepts single int for square crop, Albumentations requires both height and width\u00a0\u00a02. Padding options differ:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports padding parameter for pre-padding\u00a0\u00a0\u00a0\u00a0- Albumentations: offers pad_position parameter ('center', 'top_left', etc.)\u00a0\u00a03. Fill value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports dict mapping for different types\u00a0\u00a0\u00a0\u00a0- Albumentations: separate fill and fill_mask parameters\u00a0\u00a04. Padding modes:\u00a0\u00a0\u00a0\u00a0- TorchVision: 'constant', 'edge', 'reflect', 'symmetric'\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV border modes RandomResizedCrop RandomResizedCrop - Nearly identical functionality and parameters- Key differences:\u00a0\u00a01. TorchVision accepts single int for square output, Albumentations requires (height, width) tuple\u00a0\u00a02. Default values are the same (scale=(0.08, 1.0), ratio=(0.75, 1.3333))\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Separate mask_interpolation parameter\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomIoUCrop RandomSizedBBoxSafeCrop - Both ensure safe cropping with respect to bounding boxes- Key differences:\u00a0\u00a01. TorchVision:\u00a0\u00a0\u00a0\u00a0- Implements exact SSD paper approach\u00a0\u00a0\u00a0\u00a0- Uses IoU-based sampling strategy\u00a0\u00a0\u00a0\u00a0- Requires explicit sanitization of boxes after crop\u00a0\u00a02. Albumentations:\u00a0\u00a0\u00a0\u00a0- Simpler approach ensuring bbox safety\u00a0\u00a0\u00a0\u00a0- Directly specifies target size\u00a0\u00a0\u00a0\u00a0- Automatically handles bbox cleanup- For exact SSD-style cropping, might need custom implementation in Albumentations CenterCrop CenterCrop - Both crop the center part of the input- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: accepts single int for square crop or (height, width) tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: requires separate height and width parameters\u00a0\u00a02. Padding behavior:\u00a0\u00a0\u00a0\u00a0- TorchVision: always pads with 0 if image is smaller\u00a0\u00a0\u00a0\u00a0- Albumentations: optional padding with pad_if_needed\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Configurable padding mode and position\u00a0\u00a0\u00a0\u00a0- Separate fill values for image and mask\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomHorizontalFlip HorizontalFlip - Identical functionality- Both have default probability p=0.5- Only naming difference: TorchVision includes \"Random\" in name RandomVerticalFlip VerticalFlip - Identical functionality- Both have default probability p=0.5- Only naming difference: TorchVision includes \"Random\" in name Pad Pad - Similar core padding functionality- Both support:\u00a0\u00a0- Single int for all sides\u00a0\u00a0- (pad_x, pad_y) for symmetric padding\u00a0\u00a0- (left, top, right, bottom) for per-side padding- Key differences:\u00a0\u00a01. Padding modes:\u00a0\u00a0\u00a0\u00a0- TorchVision: 'constant', 'edge', 'reflect', 'symmetric'\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV border modes\u00a0\u00a02. Fill value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports dict mapping for different types\u00a0\u00a0\u00a0\u00a0- Albumentations: separate fill and fill_mask parameters\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomZoomOut RandomScale + PadIfNeeded - No direct equivalent in Albumentations- Can be approximated by combining: A.Compose([ A.RandomScale(scale_limit=(0.0, 3.0), p=0.5), # scale_limit=(0.0, 3.0) maps to side_range=(1.0, 4.0) A.PadIfNeeded(min_height=height, min_width=width, border_mode=cv2.BORDER_CONSTANT, value=fill) ])- Key differences:\u00a0\u00a01. TorchVision implements specific SSD paper approach\u00a0\u00a02. Albumentations requires composition of two transforms RandomRotation Rotate - Similar core rotation functionality but with different parameters- Key differences:\u00a0\u00a01. Angle specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: degrees parameter (-degrees, +degrees) or (min, max)\u00a0\u00a0\u00a0\u00a0- Albumentations: limit parameter (-limit, +limit) or (min, max)\u00a0\u00a02. Output size control:\u00a0\u00a0\u00a0\u00a0- TorchVision: expand=True/False\u00a0\u00a0\u00a0\u00a0- Albumentations: crop_border=True/False\u00a0\u00a03. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- Bbox rotation methods ('largest_box' or 'ellipse')\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a04. Center specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports custom center point\u00a0\u00a0\u00a0\u00a0- Albumentations: always uses image center RandomAffine Affine - Both support core affine operations (translation, rotation, scale, shear)- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single parameters for each transform\u00a0\u00a0\u00a0\u00a0- Albumentations: more flexible with dict options for x/y axes\u00a0\u00a02. Scale handling:\u00a0\u00a0\u00a0\u00a0- Albumentations adds keep_ratio and balanced_scale\u00a0\u00a0\u00a0\u00a0- Albumentations supports independent x/y scaling\u00a0\u00a03. Translation:\u00a0\u00a0\u00a0\u00a0- TorchVision: fraction only\u00a0\u00a0\u00a0\u00a0- Albumentations: both percent and pixels\u00a0\u00a04. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- fit_output to adjust image plane\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Bbox rotation methods\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomPerspective Perspective - Both apply random perspective transformations- Key differences:\u00a0\u00a01. Distortion control:\u00a0\u00a0\u00a0\u00a0- TorchVision: single distortion_scale (0 to 1)\u00a0\u00a0\u00a0\u00a0- Albumentations: scale tuple for corner movement range\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- Albumentations adds keep_size and fit_output options\u00a0\u00a0\u00a0\u00a0- Can control whether to maintain original size\u00a0\u00a03. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Better control over output size and fitting ElasticTransform ElasticTransform - Similar core functionality: both apply elastic deformations to images- Key differences:\u00a0\u00a01. Parameters have opposite meanings:\u00a0\u00a0\u00a0\u00a0- TorchVision: alpha (displacement), sigma (smoothness)\u00a0\u00a0\u00a0\u00a0- Albumentations: alpha (smoothness), sigma (displacement)\u00a0\u00a02. Default values reflect this difference:\u00a0\u00a0\u00a0\u00a0- TorchVision: alpha=50.0, sigma=5.0\u00a0\u00a0\u00a0\u00a0- Albumentations: alpha=1.0, sigma=50.0- Note on implementation:\u00a0\u00a0- Albumentations follows Simard et al. 2003 paper more closely:\u00a0\u00a0\u00a0\u00a0- \u03c3 should be ~0.05 * image_size\u00a0\u00a0\u00a0\u00a0- \u03b1 should be proportional to \u03c3- Additional Albumentations features:\u00a0\u00a0- approximate mode\u00a0\u00a0- same_dxdy option\u00a0\u00a0- Choice of noise distribution\u00a0\u00a0- Separate mask interpolation ColorJitter ColorJitter - Similar core functionality: both randomly adjust brightness, contrast, saturation, and hue- Key similarities:\u00a0\u00a01. Same parameter names and meanings\u00a0\u00a02. Same value ranges (e.g., hue should be in [-0.5, 0.5])\u00a0\u00a03. Random order of transformations- Key differences:\u00a0\u00a01. Default values:\u00a0\u00a0\u00a0\u00a0- TorchVision: all None by default\u00a0\u00a0\u00a0\u00a0- Albumentations: defaults to (0.8, 1.2) for brightness/contrast/saturation\u00a0\u00a02. Implementation:\u00a0\u00a0\u00a0\u00a0- TorchVision: uses Pillow\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV (may produce slightly different results)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Explicit probability parameter p\u00a0\u00a0\u00a0\u00a0- Value saturation instead of uint8 overflow RandomChannelPermutation ChannelShuffle - Both randomly permute image channels- Key similarities:\u00a0\u00a01. Same core functionality\u00a0\u00a02. Work on multi-channel images (typically RGB)- Key differences:\u00a0\u00a01. Naming convention only\u00a0\u00a02. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomPhotometricDistort RandomOrder + ColorJitter + ChannelShuffle - TorchVision's transform is from SSD paper, combines:\u00a0\u00a01. Color jittering (brightness, contrast, saturation, hue)\u00a0\u00a02. Random channel permutation- Can be replicated in Albumentations using: A.RandomOrder([ A.ColorJitter(brightness=(0.875, 1.125), contrast=(0.5, 1.5), saturation=(0.5, 1.5), hue=(-0.05, 0.05), p=0.5), A.ChannelShuffle(p=0.5) ]) Grayscale ToGray - Similar core functionality: convert RGB to grayscale- Key differences:\u00a0\u00a01. Output channels:\u00a0\u00a0\u00a0\u00a0- TorchVision: only 1 or 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: supports any number of output channels\u00a0\u00a02. Conversion methods:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method (weighted RGB)\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods via method parameter:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 weighted_average (default, same as TorchVision)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 from_lab, desaturation, average, max, pca\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- More flexible channel handling RGB ToRGB - Similar core functionality: convert to RGB format- Key differences:\u00a0\u00a01. Input handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: accepts 1 or 3 channel inputs\u00a0\u00a0\u00a0\u00a0- Albumentations: only accepts single-channel inputs\u00a0\u00a02. Output channels:\u00a0\u00a0\u00a0\u00a0- TorchVision: always 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable via num_output_channels\u00a0\u00a03. Behavior:\u00a0\u00a0\u00a0\u00a0- TorchVision: converts to RGB if not already RGB\u00a0\u00a0\u00a0\u00a0- Albumentations: strictly grayscale to RGB conversion\u00a0\u00a04. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomGrayscale ToGray - Similar core functionality: convert to grayscale with probability- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- TorchVision: p=0.1\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: always preserves input channels\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable output channels\u00a0\u00a03. Conversion methods:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods with different channel support:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 weighted_average, from_lab: 3-channel only\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 desaturation, average, max, pca: any number of channels\u00a0\u00a04. Channel requirements:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with 1 or 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: depends on method chosen GaussianBlur GaussianBlur - Similar core functionality: apply Gaussian blur with random kernel size- Key similarities:\u00a0\u00a01. Both support random kernel sizes\u00a0\u00a02. Both support random sigma values- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: kernel_size (exact size), sigma (range)\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (size range), sigma_limit (range)\u00a0\u00a02. Kernel size constraints:\u00a0\u00a0\u00a0\u00a0- TorchVision: must specify exact size\u00a0\u00a0\u00a0\u00a0- Albumentations: can specify range (3, 7) or auto-compute\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- Auto-computation of kernel size from sigma GaussianNoise GaussNoise - Similar core functionality: add Gaussian noise to images- Key similarities:\u00a0\u00a01. Both support mean and standard deviation parameters- Key differences:\u00a0\u00a01. Parameter ranges:\u00a0\u00a0\u00a0\u00a0- TorchVision: fixed values for mean and sigma\u00a0\u00a0\u00a0\u00a0- Albumentations: ranges for both (std_range, mean_range)\u00a0\u00a02. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: expects float [0,1], has clip option\u00a0\u00a0\u00a0\u00a0- Albumentations: auto-scales based on dtype\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Per-channel noise option\u00a0\u00a0\u00a0\u00a0- Noise scale factor for performance\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomInvert InvertImg - Similar core functionality: invert image colors- Key similarities:\u00a0\u00a01. Both invert pixel values\u00a0\u00a02. Both have default probability of 0.5- Key differences:\u00a0\u00a01. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with [0,1] float tensors\u00a0\u00a0\u00a0\u00a0- Albumentations: auto-handles uint8 (255) and float32 (1.0) RandomPosterize Posterize - Similar core functionality: reduce color bits- Key similarities:\u00a0\u00a01. Both posterize images with probability p=0.5- Key differences:\u00a0\u00a01. Bits specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single fixed value [0-8]\u00a0\u00a0\u00a0\u00a0- Albumentations: flexible options with [1-7] (recommended):\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Single value for all channels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Range (min_bits, max_bits)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Per-channel values [r,g,b]\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Per-channel ranges [(r_min,r_max), ...]\u00a0\u00a02. Practical range:\u00a0\u00a0\u00a0\u00a0- TorchVision: includes 0 (black) and 8 (unchanged)\u00a0\u00a0\u00a0\u00a0- Albumentations: recommended [1-7] for actual posterization RandomSolarize Solarize - Similar core functionality: invert pixels above threshold- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both invert values above threshold- Key differences:\u00a0\u00a01. Threshold specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single fixed threshold value\u00a0\u00a0\u00a0\u00a0- Albumentations: range via threshold_range\u00a0\u00a02. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with raw threshold values\u00a0\u00a0\u00a0\u00a0- Albumentations: uses normalized [0,1] range:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 uint8: multiplied by 255\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 float32: multiplied by 1.0 RandomAdjustSharpness Sharpen - Similar core functionality: adjust image sharpness- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single sharpness_factor\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 0: blurred\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 1: original\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 2: doubled sharpness\u00a0\u00a0\u00a0\u00a0- Albumentations: more controls:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 alpha: effect visibility [0,1]\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 lightness: contrast control\u00a0\u00a02. Method options:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method\u00a0\u00a0\u00a0\u00a0- Albumentations: two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 'kernel': Laplacian operator\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 'gaussian': blur interpolation RandomAutocontrast AutoContrast Same core functionality with identical parameters (p=0.5) RandomEqualize Equalize - Similar core functionality: histogram equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Choice of algorithm (cv/pil methods)\u00a0\u00a0\u00a0\u00a0- Per-channel or luminance-based equalization\u00a0\u00a0\u00a0\u00a0- Optional masking support Normalize Normalize - Similar core functionality: normalize image values- Key similarities:\u00a0\u00a01. Both support mean/std normalization- Key differences:\u00a0\u00a01. Normalization options:\u00a0\u00a0\u00a0\u00a0- TorchVision: only (input - mean) / std\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 standard (same as TorchVision)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 image (global stats)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 image_per_channel\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 min_max\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 min_max_per_channel\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- max_pixel_value parameter\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomErasing Erasing - Similar core functionality: randomly erase image regions- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Same default scale=(0.02, 0.33)\u00a0\u00a03. Same default ratio=(0.3, 3.3)- Key differences:\u00a0\u00a01. Fill value options:\u00a0\u00a0\u00a0\u00a0- TorchVision: number/tuple or 'random'\u00a0\u00a0\u00a0\u00a0- Albumentations: additional options:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 random_uniform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 inpaint_telea\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 inpaint_ns\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Mask fill value option\u00a0\u00a0\u00a0\u00a0- Support for masks, bboxes, keypoints JPEG ImageCompression - Similar core functionality: apply JPEG compression- Key similarities:\u00a0\u00a01. Both use quality range 1-100\u00a0\u00a02. Both support quality ranges- Key differences:\u00a0\u00a01. Compression types:\u00a0\u00a0\u00a0\u00a0- TorchVision: JPEG only\u00a0\u00a0\u00a0\u00a0- Albumentations: JPEG and WebP\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- Default quality range (99, 100)"},{"location":"getting_started/augmentation_mapping/#kornia-to-albumentations","title":"Kornia to Albumentations","text":"Kornia Albumentations Notes ColorJitter ColorJitter - Similar core functionality: randomly adjust brightness, contrast, saturation, and hue- Key similarities:\u00a0\u00a01. Both support same parameters (brightness, contrast, saturation, hue)\u00a0\u00a02. Both allow float or tuple ranges for parameters- Key differences:\u00a0\u00a01. Default values:\u00a0\u00a0\u00a0\u00a0- Albumentations: (0.8, 1.2) for brightness/contrast/saturation\u00a0\u00a0\u00a0\u00a0- Kornia: 0.0 for all parameters\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a03. Note: Kornia recommends using ColorJiggle instead as it follows color theory better RandomAutoContrast AutoContrast - Similar core functionality: enhance image contrast automatically- Key similarities:\u00a0\u00a01. Both stretch intensity range to use full range\u00a0\u00a02. Both preserve relative intensities- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter to control value clipping RandomBoxBlur Blur - Similar core functionality: apply box/average blur to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both apply box/average blur filter- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit parameter for range (e.g., (3, 7))\u00a0\u00a0\u00a0\u00a0- Kornia: fixed kernel_size tuple (default (3, 3))\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- border_type parameter ('reflect', 'replicate', 'circular')\u00a0\u00a0\u00a0\u00a0- normalized parameter for L1 norm control RandomBrightness RandomBrightnessContrast - Different scope:\u00a0\u00a0- Kornia: brightness only\u00a0\u00a0- Albumentations: combines brightness and contrast- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: brightness tuple (default: (1.0, 1.0))\u00a0\u00a0\u00a0\u00a0- Albumentations: brightness_limit (default: (-0.2, 0.2))\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- brightness_by_max parameter for adjustment method\u00a0\u00a0\u00a0\u00a0- ensure_safe_range to prevent overflow/underflow\u00a0\u00a0\u00a0\u00a0- Combined contrast control\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter RandomChannelDropout ChannelDropout - Similar core functionality: randomly drop image channels- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both allow specifying fill value for dropped channels- Key differences:\u00a0\u00a01. Channel drop specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed num_drop_channels (default: 1)\u00a0\u00a0\u00a0\u00a0- Albumentations: flexible channel_drop_range tuple (default: (1, 1))\u00a0\u00a02. Error handling:\u00a0\u00a0\u00a0\u00a0- Albumentations: explicit checks for single-channel images and invalid ranges\u00a0\u00a0\u00a0\u00a0- Kornia: simpler parameter validation RandomChannelShuffle ChannelShuffle - Identical core functionality: randomly shuffle image channels RandomClahe CLAHE - Similar core functionality: apply Contrast Limited Adaptive Histogram Equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both allow configuring grid size and clip limit- Key differences:\u00a0\u00a01. Parameter defaults:\u00a0\u00a0\u00a0\u00a0- Kornia: clip_limit=(40.0, 40.0), grid_size=(8, 8)\u00a0\u00a0\u00a0\u00a0- Albumentations: clip_limit=(1, 4), tile_grid_size=(8, 8)\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- slow_and_differentiable parameter for implementation choice RandomContrast RandomBrightnessContrast - Different scope:\u00a0\u00a0- Kornia: contrast only\u00a0\u00a0- Albumentations: combines brightness and contrast- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: contrast tuple (default: (1.0, 1.0))\u00a0\u00a0\u00a0\u00a0- Albumentations: contrast_limit (default: (-0.2, 0.2))\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- ensure_safe_range to prevent overflow/underflow\u00a0\u00a0\u00a0\u00a0- Combined brightness control\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter RandomEqualize Equalize - Similar core functionality: apply histogram equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- mode parameter to choose between 'cv' and 'pil' methods\u00a0\u00a0\u00a0\u00a0- by_channels parameter for per-channel or luminance-based equalization\u00a0\u00a0\u00a0\u00a0- mask parameter to selectively apply equalization\u00a0\u00a0\u00a0\u00a0- mask_params for dynamic mask generation RandomGamma RandomGamma - Similar core functionality: apply random gamma correction- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: separate gamma (1.0, 1.0) and gain (1.0, 1.0) tuples\u00a0\u00a0\u00a0\u00a0- Albumentations: single gamma_limit (80, 120) as percentage range\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- eps parameter to prevent numerical errors RandomGaussianBlur GaussianBlur - Similar core functionality: apply Gaussian blur with random parameters- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support kernel size and sigma parameters- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: requires explicit kernel_size and sigma range\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (default: (3, 7)) and sigma_limit (default: 0)\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- border_type parameter for padding mode\u00a0\u00a0\u00a0\u00a0- separable parameter for 1D convolution optimization RandomGaussianIllumination Illumination - Similar core functionality: apply illumination effects- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support controlling effect intensity and position- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: Gaussian illumination patterns only\u00a0\u00a0\u00a0\u00a0- Albumentations: Multiple modes (linear, corner, gaussian)\u00a0\u00a02. Parameter ranges:\u00a0\u00a0\u00a0\u00a0- Kornia: gain=(0.01, 0.15), center=(0.1, 0.9), sigma=(0.2, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range=(0.01, 0.2), center_range=(0.1, 0.9), sigma_range=(0.2, 1.0)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- mode parameter for different effect types\u00a0\u00a0\u00a0\u00a0- effect_type for brighten/darken control\u00a0\u00a0\u00a0\u00a0- angle_range for linear gradients\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- sign parameter for effect direction RandomGaussianNoise GaussNoise - Similar core functionality: add Gaussian noise to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed mean (default: 0.0) and std (default: 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: ranges via std_range (0.2, 0.44) and mean_range (0.0, 0.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- per_channel parameter for independent channel noise\u00a0\u00a0\u00a0\u00a0- noise_scale_factor for performance optimization\u00a0\u00a0\u00a0\u00a0- Automatic value scaling based on image dtype RandomGrayscale ToGray - Similar core functionality: convert images to grayscale- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=0.1\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a02. Conversion options:\u00a0\u00a0\u00a0\u00a0- Kornia: customizable rgb_weights for channel mixing\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple method options (weighted_average, from_lab, desaturation, average, max, pca)\u00a0\u00a03. Output control:\u00a0\u00a0\u00a0\u00a0- Kornia: always 3-channel output\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable num_output_channels RandomHue ColorJitter (hue parameter) - Similar core functionality: adjust image hue- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: hue-only transform\u00a0\u00a0\u00a0\u00a0- Albumentations: part of ColorJitter with brightness, contrast, and saturation\u00a0\u00a02. Default values:\u00a0\u00a0\u00a0\u00a0- Kornia: hue=(0.0, 0.0), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: hue=(-0.5, 0.5), p=0.5 RandomInvert InvertImg - Similar core functionality: invert image values- Key differences:\u00a0\u00a01. Maximum value handling:\u00a0\u00a0\u00a0\u00a0- Kornia: configurable via max_val parameter (default: 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: automatically determined by dtype (255 for uint8, 1.0 for float32) RandomJPEG ImageCompression - Similar core functionality: apply image compression- Key differences:\u00a0\u00a01. Compression options:\u00a0\u00a0\u00a0\u00a0- Kornia: JPEG only\u00a0\u00a0\u00a0\u00a0- Albumentations: supports both JPEG and WebP\u00a0\u00a02. Quality specification:\u00a0\u00a0\u00a0\u00a0- Kornia: jpeg_quality (default: 50.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: quality_range (default: (99, 100))\u00a0\u00a03. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5 RandomLinearCornerIllumination Illumination (corner mode) - Similar core functionality: apply corner illumination effects- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: corner illumination only\u00a0\u00a0\u00a0\u00a0- Albumentations: part of general Illumination transform with multiple modes\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: gain (0.01, 0.2) and sign (-1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range (0.01, 0.2) and effect_type (brighten/darken/both)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Multiple illumination modes (linear, corner, gaussian)\u00a0\u00a0\u00a0\u00a0- More control over effect parameters RandomLinearIllumination Illumination (linear mode) - Similar core functionality: apply linear illumination effects- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: linear illumination only\u00a0\u00a0\u00a0\u00a0- Albumentations: part of general Illumination transform with multiple modes\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: gain (0.01, 0.2) and sign (-1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range (0.01, 0.2), effect_type (brighten/darken/both), and angle_range (0, 360)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Multiple illumination modes (linear, corner, gaussian)\u00a0\u00a0\u00a0\u00a0- Explicit angle control for gradient direction RandomMedianBlur MedianBlur - Similar core functionality: apply median blur filter- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed kernel_size tuple (default: (3, 3))\u00a0\u00a0\u00a0\u00a0- Albumentations: range via blur_limit (default: (3, 7))\u00a0\u00a02. Kernel constraints:\u00a0\u00a0\u00a0\u00a0- Albumentations: enforces odd kernel sizes RandomMotionBlur MotionBlur - Similar core functionality: apply directional motion blur- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support angle and direction control- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: kernel_size as int or tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (default: (3, 7))\u00a0\u00a02. Angle control:\u00a0\u00a0\u00a0\u00a0- Kornia: angle parameter with symmetric range (-angle, angle)\u00a0\u00a0\u00a0\u00a0- Albumentations: angle_range (default: (0, 360))\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- allow_shifted parameter for kernel position control RandomPlanckianJitter PlanckianJitter - Similar core functionality: apply physics-based color temperature variations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support 'blackbody' and 'cied' modes- Key differences:\u00a0\u00a01. Temperature control:\u00a0\u00a0\u00a0\u00a0- Kornia: select_from parameter for discrete jitter selection\u00a0\u00a0\u00a0\u00a0- Albumentations: temperature_limit for continuous range\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- sampling_method parameter ('uniform' or 'gaussian')\u00a0\u00a0\u00a0\u00a0- More detailed control over temperature ranges\u00a0\u00a0\u00a0\u00a0- Better documentation of physics-based effects RandomPlasmaBrightness PlasmaBrightnessContrast - Similar core functionality: apply fractal-based brightness adjustments- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7) and intensity (0.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: brightness_range (-0.3, 0.3), contrast_range (-0.3, 0.3), roughness (default: 3.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Combined brightness and contrast adjustment\u00a0\u00a0\u00a0\u00a0- plasma_size parameter for pattern detail control\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation and documentation RandomPlasmaContrast PlasmaBrightnessContrast - Similar core functionality: apply fractal-based contrast adjustments- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7) only\u00a0\u00a0\u00a0\u00a0- Albumentations: contrast_range (-0.3, 0.3), roughness (default: 3.0), plasma_size (default: 256)\u00a0\u00a02. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: contrast-only adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: combined brightness and contrast adjustment\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation\u00a0\u00a0\u00a0\u00a0- Pattern size control via plasma_size RandomPlasmaShadow PlasmaShadow - Similar core functionality: apply fractal-based shadow effects- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7), shade_intensity (-1.0, 0.0), shade_quantity (0.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: shadow_intensity_range (0.3, 0.7), plasma_size (default: 256), roughness (default: 3.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Pattern size control via plasma_size\u00a0\u00a0\u00a0\u00a0- More intuitive intensity range (0 to 1)\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation and documentation RandomPosterize Posterize - Similar core functionality: reduce color bits in image- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both operate on color bit reduction- Key differences:\u00a0\u00a01. Bit specification:\u00a0\u00a0\u00a0\u00a0- Kornia: bits parameter (default: 3) with range (0, 8], can be float or tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: num_bits parameter (default: 4) with range [1, 7], supports multiple formats:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single int for all channels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Tuple for random range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* List for per-channel specification\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* List of tuples for per-channel ranges\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- More flexible channel-wise control\u00a0\u00a0\u00a0\u00a0- More detailed documentation and mathematical background RandomRain RandomRain - Similar core functionality: add rain effects to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Rain parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: number_of_drops (1000, 2000), drop_height (5, 20), drop_width (-5, 5)\u00a0\u00a0\u00a0\u00a0- Albumentations: slant_range (-10, 10), drop_length (20), drop_width (1)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- drop_color customization\u00a0\u00a0\u00a0\u00a0- blur_value for atmospheric effect\u00a0\u00a0\u00a0\u00a0- brightness_coefficient for lighting adjustment\u00a0\u00a0\u00a0\u00a0- rain_type presets (drizzle, heavy, torrential)\u00a0\u00a03. Approach:\u00a0\u00a0\u00a0\u00a0- Kornia: Direct drop placement\u00a0\u00a0\u00a0\u00a0- Albumentations: More realistic simulation with slant, blur, and brightness effects RandomRGBShift AdditiveNoise - Similar core functionality: add noise/shifts to image channels- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both can affect individual channels- Key differences:\u00a0\u00a01. Approach:\u00a0\u00a0\u00a0\u00a0- Kornia: Simple RGB channel shifts with individual limits\u00a0\u00a0\u00a0\u00a0- Albumentations: More sophisticated noise generation with multiple distributions\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: r_shift_limit, g_shift_limit, b_shift_limit (all default: 0.5)\u00a0\u00a0\u00a0\u00a0- Albumentations: Flexible noise configuration with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple noise types (uniform, gaussian, laplace, beta)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Different spatial modes (constant, per_pixel, shared)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Customizable distribution parameters\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Performance optimization options\u00a0\u00a0\u00a0\u00a0- More detailed control over noise distribution\u00a0\u00a0\u00a0\u00a0- Spatial application modes RandomSaltAndPepperNoise SaltAndPepper - Similar core functionality: apply salt and pepper noise to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use same default parameters:\u00a0\u00a0\u00a0\u00a0- amount (0.01, 0.06)\u00a0\u00a0\u00a0\u00a0- salt_vs_pepper (0.4, 0.6)- Key differences:\u00a0\u00a01. Parameter flexibility:\u00a0\u00a0\u00a0\u00a0- Kornia: Supports single float or tuple for parameters\u00a0\u00a0\u00a0\u00a0- Albumentations: Requires tuples for ranges\u00a0\u00a02. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Detailed mathematical formulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Clear examples for different noise levels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Implementation notes and edge cases\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* References to academic sources RandomSaturation ColorJitter - Different scope and functionality:- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: Saturation-only adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: Combined brightness, contrast, saturation, and hue adjustment\u00a0\u00a02. Default parameters:\u00a0\u00a0\u00a0\u00a0- Kornia: saturation (1.0, 1.0), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: saturation (0.8, 1.2), p=0.5\u00a0\u00a03. Implementation:\u00a0\u00a0\u00a0\u00a0- Kornia: Aligns with PIL/TorchVision implementation\u00a0\u00a0\u00a0\u00a0- Albumentations: Uses OpenCV with noted differences in HSV conversion\u00a0\u00a04. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Brightness adjustment\u00a0\u00a0\u00a0\u00a0- Contrast adjustment\u00a0\u00a0\u00a0\u00a0- Hue adjustment\u00a0\u00a0\u00a0\u00a0- Random order of transformations RandomSharpness Sharpen - Similar core functionality: sharpen images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single sharpness parameter (default: 0.5)\u00a0\u00a0\u00a0\u00a0- Albumentations: More detailed control with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* alpha (0.2, 0.5) for effect visibility\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* lightness (0.5, 1.0) for contrast\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* method choice ('kernel' or 'gaussian')\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* kernel_size and sigma for gaussian method\u00a0\u00a02. Implementation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: Single approach\u00a0\u00a0\u00a0\u00a0- Albumentations: Two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Kernel-based using Laplacian operator\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Gaussian interpolation\u00a0\u00a03. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides detailed mathematical formulation and references RandomSnow RandomSnow - Similar core functionality: add snow effects to images- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: snow_coefficient (0.5, 0.5), brightness (2, 2), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: snow_point_range (0.1, 0.3), brightness_coeff (2.5), p=0.5\u00a0\u00a02. Implementation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: Single approach\u00a0\u00a0\u00a0\u00a0- Albumentations: Two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"bleach\": Simple pixel value thresholding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"texture\": Advanced snow texture simulation\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Detailed snow simulation with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* HSV color space manipulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Gaussian noise for texture\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Depth effect simulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Sparkle effects\u00a0\u00a04. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides detailed mathematical formulation and implementation notes RandomSolarize Solarize - Similar core functionality: invert pixel values above threshold- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Two parameters:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* thresholds (default: 0.1) for threshold range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* additions (default: 0.1) for value adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: Single parameter:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* threshold_range (default: (0.5, 0.5))\u00a0\u00a02. Threshold handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Generates from (0.5 - x, 0.5 + x) for float input\u00a0\u00a0\u00a0\u00a0- Albumentations: Direct range specification, scaled by image type max value\u00a0\u00a03. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Detailed examples for both uint8 and float32 images\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Clear mathematical formulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Image type-specific behavior explanation CenterCrop CenterCrop - Similar core functionality: crop center of image- Key similarities:\u00a0\u00a01. Both have default probability p=1.0- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size parameter (int or tuple)\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate height and width parameters\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners for interpolation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* resample mode selection\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* cropping_mode ('slice' or 'resample')\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* pad_if_needed for handling small images\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* border_mode for padding method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* fill and fill_mask for padding values\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* pad_position options\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Supports images, masks, bboxes, and keypoints PadTo PadIfNeeded - Can achieve same core functionality- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Can pad to exact size:\u00a0\u00a0\u00a0\u00a0- Kornia: size=(height, width)\u00a0\u00a0\u00a0\u00a0- Albumentations: min_height=height, min_width=width- Key differences:\u00a0\u00a01. Parameter naming:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate dimension parameters\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simple pad_mode selection\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single pad_value\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible position options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate fill and fill_mask\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Optional divisibility padding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple target support\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomAffine Affine - Similar core functionality: apply affine transformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support rotation, translation, scaling, and shear- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* degrees for rotation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* translate as fraction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale as tuple\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear in degrees\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More flexible parameter formats\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Supports both percent and pixel translation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Dictionary format for independent axis control\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* fit_output for automatic size adjustment\u00a0\u00a0\u00a0\u00a0* keep_ratio for aspect ratio preservation\u00a0\u00a0\u00a0\u00a0* rotate_method options\u00a0\u00a0\u00a0\u00a0* balanced_scale for even scale distribution\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomCrop RandomCrop - Similar core functionality: randomly crop image patches- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Both support padding if needed- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size tuple (height, width)\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate height and width parameters\u00a0\u00a02. Padding options:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible padding sizes (int, tuple[2], tuple[4])\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple padding modes (constant, reflect, replicate)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single fill value\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler padding interface\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate fill values for image and mask\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible pad positioning\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomElasticTransform ElasticTransform - Similar core functionality: apply elastic deformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Gaussian smoothing for displacement fields\u00a0\u00a03. Both support independent control of x/y deformations:\u00a0\u00a0\u00a0\u00a0- Kornia: via separate values in sigma/alpha tuples\u00a0\u00a0\u00a0\u00a0- Albumentations: via same_dxdy parameter- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* kernel_size tuple (63, 63)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* sigma tuple (32.0, 32.0)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* alpha tuple (1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single sigma (default: 50.0)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single alpha (default: 1.0)\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Control over padding mode\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* approximate mode for faster processing\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Choice of noise distribution\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomErasing Erasing - Similar core functionality: randomly erase rectangular regions- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Same default parameters:\u00a0\u00a0\u00a0\u00a0* scale (0.02, 0.33)\u00a0\u00a0\u00a0\u00a0* ratio (0.3, 3.3)- Key differences:\u00a0\u00a01. Fill value options:\u00a0\u00a0\u00a0\u00a0- Kornia: Simple numeric value (default: 0.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: Rich fill options:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Numeric values\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"random\" per pixel\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"random_uniform\" per region\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"inpaint_telea\" method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"inpaint_ns\" method\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate mask_fill value\u00a0\u00a0\u00a0\u00a0* Support for masks, bboxes, keypoints\u00a0\u00a0\u00a0\u00a0* Inpainting options for more natural-looking results\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomFisheye OpticalDistortion - Similar core functionality: apply optical/fisheye distortion- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support fisheye distortion- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate center_x, center_y for distortion center\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* gamma for distortion strength\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single distort_limit parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* mode selection ('camera' or 'fisheye')\u00a0\u00a02. Distortion models:\u00a0\u00a0\u00a0\u00a0- Kornia: Fisheye only\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Camera matrix model\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fisheye model\u00a0\u00a03. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate interpolation methods for image and mask\u00a0\u00a0\u00a0\u00a0* Support for masks, bboxes, keypoints\u00a0\u00a04. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomHorizontalFlip HorizontalFlip - Similar core functionality: flip image horizontally- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Simple operation with same visual result- Key differences:\u00a0\u00a01. Batch handling:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Additional p_batch parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* same_on_batch option\u00a0\u00a0\u00a0\u00a0- Albumentations: No batch-specific parameters\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomPerspective Perspective - Similar core functionality: apply perspective transformation- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both transform image by moving corners\u00a0\u00a03. Both support different interpolation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: via resample (BILINEAR, NEAREST)\u00a0\u00a0\u00a0\u00a0- Albumentations: via interpolation (INTER_LINEAR, INTER_NEAREST, etc.)- Key differences:\u00a0\u00a01. Distortion control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* distortion_scale (0 to 1, default: 0.5)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* sampling_method ('basic' or 'area_preserving')\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale tuple for corner movement range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* fit_output option for image capture\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keepdim for batch form\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keep_size for output dimensions\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Border mode and fill options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomResizedCrop RandomResizedCrop - Similar core functionality: crop random patches and resize- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Same default parameters:\u00a0\u00a0\u00a0\u00a0* scale (0.08, 1.0)\u00a0\u00a0\u00a0\u00a0* ratio (~0.75, ~1.33)\u00a0\u00a03. Both support different interpolation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: via resample\u00a0\u00a0\u00a0\u00a0- Albumentations: via interpolation- Key differences:\u00a0\u00a01. Implementation options:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* cropping_mode ('slice' or 'resample')\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keepdim for batch form\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fallback to center crop after 10 attempts\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomRotation90 RandomRotate90 - Similar core functionality: rotate image by 90 degrees- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both rotate in 90-degree increments- Key differences:\u00a0\u00a01. Rotation control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* times parameter to specify range of rotations\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* resample and align_corners for interpolation\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler implementation (0-3 rotations)\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomRotation Rotate - Similar core functionality: rotate image by random angle- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support different interpolation methods- Key differences:\u00a0\u00a01. Angle specification:\u00a0\u00a0\u00a0\u00a0- Kornia: degrees parameter (if single value, range is (-degrees, +degrees))\u00a0\u00a0\u00a0\u00a0- Albumentations: limit parameter (default: (-90, 90))\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners for interpolation\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Border mode options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fill values for padding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* rotate_method for bboxes\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* crop_border option\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomShear Affine (shear parameter) - Similar core functionality: apply shear transformation- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support different interpolation methods\u00a0\u00a03. Both support independent x/y shear control- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Dedicated shear transform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear parameter supports float, tuple(2), or tuple(4)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simple padding modes (zeros, border, reflection)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Part of general Affine transform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear supports number, tuple, or dict format\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More border modes and fill options\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a0\u00a0\u00a0* fit_output option\u00a0\u00a0\u00a0\u00a0* Combined with other affine transforms\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomThinPlateSpline ThinPlateSpline - Similar core functionality: apply smooth, non-rigid deformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use thin plate spline algorithm\u00a0\u00a03. Both support interpolation options- Key differences:\u00a0\u00a01. Deformation control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single scale parameter (default: 0.2)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fixed control point grid\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale_range tuple for range of deformation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Configurable num_control_points\u00a0\u00a02. Implementation details:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Binary mode choice (bilinear/nearest)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* OpenCV interpolation flags\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More granular control over grid\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomVerticalFlip VerticalFlip - Similar core functionality: flip image vertically- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Simple operation with same visual result- Key differences:\u00a0\u00a01. Implementation:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Additional p_batch parameter\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler implementation\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints"},{"location":"getting_started/augmentation_mapping/#key-differences_1","title":"Key Differences","text":""},{"location":"getting_started/augmentation_mapping/#compared-to-torchvision_1","title":"Compared to TorchVision","text":"
  • Albumentations operates on numpy arrays instead of PyTorch tensors
  • Albumentations typically provides more parameters for fine-tuning transformations
  • Most Albumentations transforms support both image and mask augmentation
  • Better support for bounding box and keypoint augmentation
"},{"location":"getting_started/augmentation_mapping/#compared-to-kornia_1","title":"Compared to Kornia","text":"
  • Kornia operates directly on GPU tensors, while Albumentations works with numpy arrays
  • Albumentations provides more comprehensive support for object detection and segmentation tasks
  • Albumentations typically offers better performance for CPU-based augmentations
"},{"location":"getting_started/augmentation_mapping/#performance-comparison","title":"Performance Comparison","text":"

According to benchmarking results, Albumentations generally offers superior CPU performance compared to TorchVision and Kornia for most transforms. Here are some key highlights: Common Transforms Performance (images/second, higher is better)

Transform Albumentations TorchVision Kornia Notes HorizontalFlip 8,618 914 390 Albumentations is ~9x faster than TorchVision, ~22x faster than Kornia VerticalFlip 22,847 3,198 1,212 Albumentations is ~7x faster than TorchVision, ~19x faster than Kornia RandomResizedCrop 2,828 511 287 Albumentations is ~5.5x faster than TorchVision, ~10x faster than Kornia Normalize 1,196 519 626 Albumentations is ~2x faster than both ColorJitter 628 46 55 Albumentations is ~13x faster than both"},{"location":"getting_started/augmentation_mapping/#key-performance-insights","title":"Key Performance Insights:","text":"
  • Basic Operations: Albumentations excels at basic transforms like flips and crops, often being 5-20x faster than alternatives
  • Complex Operations: For more complex transforms like elastic deformation, the performance gap narrows
  • Memory Efficiency: Working with numpy arrays (Albumentations) is generally more memory efficient than tensor operations (Kornia/TorchVision) on CPU
"},{"location":"getting_started/augmentation_mapping/#when-to-choose-each-library","title":"When to Choose Each Library:","text":"
  • Albumentations: Best choice for CPU-based preprocessing pipelines and when maximum performance is needed
  • Kornia: Consider when doing augmentation on GPU with existing PyTorch tensors
  • TorchVision: Good choice when deeply integrated into PyTorch ecosystem and GPU performance isn't critical

Note: Benchmarks performed on macOS-15.0.1-arm64 with Python 3.12.7. Your results may vary based on hardware and setup.

"},{"location":"getting_started/augmentation_mapping/#code-examples","title":"Code Examples","text":""},{"location":"getting_started/augmentation_mapping/#torchvision-to-albumentations","title":"TorchVision to Albumentations","text":"Python
# TorchVision\ntransforms = T.Compose([\n    T.RandomHorizontalFlip(p=0.5),\n    T.RandomRotation(10),\n    T.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n\n# Albumentations equivalent\ntransforms = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.Rotate(limit=10),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n
"},{"location":"getting_started/augmentation_mapping/#kornia-to-albumentations_1","title":"Kornia to Albumentations","text":"Python
# Kornia\ntransforms = K.AugmentationSequential(\n    K.RandomHorizontalFlip(p=0.5),\n    K.RandomRotation(degrees=10),\n    K.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n)\n\n# Albumentations equivalent\ntransforms = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.Rotate(limit=10),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n
"},{"location":"getting_started/augmentation_mapping/#additional-resources","title":"Additional Resources","text":"
  • TorchVision Transforms Documentation
  • Kornia Augmentation Documentation
  • Albumentations Documentation
"},{"location":"getting_started/bounding_boxes_augmentation/","title":"Bounding boxes augmentation for object detection","text":""},{"location":"getting_started/bounding_boxes_augmentation/#different-annotations-formats","title":"Different annotations formats","text":"

Bounding boxes are rectangles that mark objects on an image. There are multiple formats of bounding boxes annotations. Each format uses its specific representation of bounding boxes coordinates. Albumentations supports four formats: pascal_voc, albumentations, coco, and yolo .

Let's take a look at each of those formats and how they represent coordinates of bounding boxes.

As an example, we will use an image from the dataset named Common Objects in Context. It contains one bounding box that marks a cat. The image width is 640 pixels, and its height is 480 pixels. The width of the bounding box is 322 pixels, and its height is 117 pixels.

The bounding box has the following (x, y) coordinates of its corners: top-left is (x_min, y_min) or (98px, 345px), top-right is (x_max, y_min) or (420px, 345px), bottom-left is (x_min, y_max) or (98px, 462px), bottom-right is (x_max, y_max) or (420px, 462px). As you see, coordinates of the bounding box's corners are calculated with respect to the top-left corner of the image which has (x, y) coordinates (0, 0).

An example image with a bounding box from the COCO dataset

"},{"location":"getting_started/bounding_boxes_augmentation/#pascal_voc","title":"pascal_voc","text":"

pascal_voc is a format used by the Pascal VOC dataset. Coordinates of a bounding box are encoded with four values in pixels: [x_min, y_min, x_max, y_max]. x_min and y_min are coordinates of the top-left corner of the bounding box. x_max and y_max are coordinates of bottom-right corner of the bounding box.

Coordinates of the example bounding box in this format are [98, 345, 420, 462].

"},{"location":"getting_started/bounding_boxes_augmentation/#albumentations","title":"albumentations","text":"

albumentations is similar to pascal_voc, because it also uses four values [x_min, y_min, x_max, y_max] to represent a bounding box. But unlike pascal_voc, albumentations uses normalized values. To normalize values, we divide coordinates in pixels for the x- and y-axis by the width and the height of the image.

Coordinates of the example bounding box in this format are [98 / 640, 345 / 480, 420 / 640, 462 / 480] which are [0.153125, 0.71875, 0.65625, 0.9625].

Albumentations uses this format internally to work with bounding boxes and augment them.

"},{"location":"getting_started/bounding_boxes_augmentation/#coco","title":"coco","text":"

coco is a format used by the Common Objects in Context COCO dataset.

In coco, a bounding box is defined by four values in pixels [x_min, y_min, width, height]. They are coordinates of the top-left corner along with the width and height of the bounding box.

Coordinates of the example bounding box in this format are [98, 345, 322, 117].

"},{"location":"getting_started/bounding_boxes_augmentation/#yolo","title":"yolo","text":"

In yolo, a bounding box is represented by four values [x_center, y_center, width, height]. x_center and y_center are the normalized coordinates of the center of the bounding box. To make coordinates normalized, we take pixel values of x and y, which marks the center of the bounding box on the x- and y-axis. Then we divide the value of x by the width of the image and value of y by the height of the image. width and height represent the width and the height of the bounding box. They are normalized as well.

Coordinates of the example bounding box in this format are [((420 + 98) / 2) / 640, ((462 + 345) / 2) / 480, 322 / 640, 117 / 480] which are [0.4046875, 0.840625, 0.503125, 0.24375].

How different formats represent coordinates of a bounding box

"},{"location":"getting_started/bounding_boxes_augmentation/#bounding-boxes-augmentation","title":"Bounding boxes augmentation","text":"

Just like with images and masks augmentation, the process of augmenting bounding boxes consists of 4 steps.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and bounding boxes from the disk.
  4. You pass an image and bounding boxes to the augmentation pipeline and receive augmented images and boxes.

Note

Some transforms in Albumentation don't support bounding boxes. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment bounding boxes.

"},{"location":"getting_started/bounding_boxes_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"Python
import albumentations as A\nimport cv2\n
"},{"location":"getting_started/bounding_boxes_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

Here an example of a minimal declaration of an augmentation pipeline that works with bounding boxes.

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco'))\n

Note that unlike image and masks augmentation, Compose now has an additional parameter bbox_params. You need to pass an instance of A.BboxParams to that argument. A.BboxParams specifies settings for working with bounding boxes. format sets the format for bounding boxes coordinates.

It can either be pascal_voc, albumentations, coco or yolo. This value is required because Albumentation needs to know the coordinates' source format for bounding boxes to apply augmentations correctly.

Besides format, A.BboxParams supports a few more settings.

Here is an example of Compose that shows all available settings with A.BboxParams:

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco', min_area=1024, min_visibility=0.1, label_fields=['class_labels']))\n
"},{"location":"getting_started/bounding_boxes_augmentation/#min_area-and-min_visibility","title":"min_area and min_visibility","text":"

min_area and min_visibility parameters control what Albumentations should do to the augmented bounding boxes if their size has changed after augmentation. The size of bounding boxes could change if you apply spatial augmentations, for example, when you crop a part of an image or when you resize an image.

min_area is a value in pixels. If the area of a bounding box after augmentation becomes smaller than min_area, Albumentations will drop that box. So the returned list of augmented bounding boxes won't contain that bounding box.

min_visibility is a value between 0 and 1. If the ratio of the bounding box area after augmentation to the area of the bounding box before augmentation becomes smaller than min_visibility, Albumentations will drop that box. So if the augmentation process cuts the most of the bounding box, that box won't be present in the returned list of the augmented bounding boxes.

Here is an example image that contains two bounding boxes. Bounding boxes coordinates are declared using the coco format.

An example image with two bounding boxes

First, we apply the CenterCrop augmentation without declaring parameters min_area and min_visibility. The augmented image contains two bounding boxes.

An example image with two bounding boxes after applying augmentation

Next, we apply the same CenterCrop augmentation, but now we also use the min_area parameter. Now, the augmented image contains only one bounding box, because the other bounding box's area after augmentation became smaller than min_area, so Albumentations dropped that bounding box.

An example image with one bounding box after applying augmentation with 'min_area'

Finally, we apply the CenterCrop augmentation with the min_visibility. After that augmentation, the resulting image doesn't contain any bounding box, because visibility of all bounding boxes after augmentation are below threshold set by min_visibility.

An example image with zero bounding boxes after applying augmentation with 'min_visibility'

"},{"location":"getting_started/bounding_boxes_augmentation/#class-labels-for-bounding-boxes","title":"Class labels for bounding boxes","text":"

Besides coordinates, each bounding box should have an associated class label that tells which object lies inside the bounding box. There are two ways to pass a label for a bounding box.

Let's say you have an example image with three objects: dog, cat, and sports ball. Bounding boxes coordinates in the coco format for those objects are [23, 74, 295, 388], [377, 294, 252, 161], and [333, 421, 49, 49].

An example image with 3 bounding boxes from the COCO dataset

"},{"location":"getting_started/bounding_boxes_augmentation/#1-you-can-pass-labels-along-with-bounding-boxes-coordinates-by-adding-them-as-additional-values-to-the-list-of-coordinates","title":"1. You can pass labels along with bounding boxes coordinates by adding them as additional values to the list of coordinates.","text":"

For the image above, bounding boxes with class labels will become [23, 74, 295, 388, 'dog'], [377, 294, 252, 161, 'cat'], and [333, 421, 49, 49, 'sports ball'].

Class labels could be of any type: integer, string, or any other Python data type. For example, integer values as class labels will look the following: [23, 74, 295, 388, 18], [377, 294, 252, 161, 17], and [333, 421, 49, 49, 37].

Also, you can use multiple class values for each bounding box, for example [23, 74, 295, 388, 'dog', 'animal'], [377, 294, 252, 161, 'cat', 'animal'], and [333, 421, 49, 49, 'sports ball', 'item'].

"},{"location":"getting_started/bounding_boxes_augmentation/#2you-can-pass-labels-for-bounding-boxes-as-a-separate-list-the-preferred-way","title":"2.You can pass labels for bounding boxes as a separate list (the preferred way).","text":"

For example, if you have three bounding boxes like [23, 74, 295, 388], [377, 294, 252, 161], and [333, 421, 49, 49] you can create a separate list with values like ['cat', 'dog', 'sports ball'], or [18, 17, 37] that contains class labels for those bounding boxes. Next, you pass that list with class labels as a separate argument to the transform function. Albumentations needs to know the names of all those lists with class labels to join them with augmented bounding boxes correctly. Then, if a bounding box is dropped after augmentation because it is no longer visible, Albumentations will drop the class label for that box as well. Use label_fields parameter to set names for all arguments in transform that will contain label descriptions for bounding boxes (more on that in Step 4).

"},{"location":"getting_started/bounding_boxes_augmentation/#step-3-read-images-and-bounding-boxes-from-the-disk","title":"Step 3. Read images and bounding boxes from the disk.","text":"

Read an image from the disk.

Python
image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n

Bounding boxes can be stored on the disk in different serialization formats: JSON, XML, YAML, CSV, etc. So the code to read bounding boxes depends on the actual format of data on the disk.

After you read the data from the disk, you need to prepare bounding boxes for Albumentations.

Albumentations expects that bounding boxes will be represented as a list of lists. Each list contains information about a single bounding box. A bounding box definition should have at list four elements that represent the coordinates of that bounding box. The actual meaning of those four values depends on the format of bounding boxes (either pascal_voc, albumentations, coco, or yolo). Besides four coordinates, each definition of a bounding box may contain one or more extra values. You can use those extra values to store additional information about the bounding box, such as a class label of the object inside the box. During augmentation, Albumentations will not process those extra values. The library will return them as is along with the updated coordinates of the augmented bounding box.

"},{"location":"getting_started/bounding_boxes_augmentation/#step-4-pass-an-image-and-bounding-boxes-to-the-augmentation-pipeline-and-receive-augmented-images-and-boxes","title":"Step 4. Pass an image and bounding boxes to the augmentation pipeline and receive augmented images and boxes.","text":"

As discussed in Step 2, there are two ways of passing class labels along with bounding boxes coordinates:

"},{"location":"getting_started/bounding_boxes_augmentation/#1-pass-class-labels-along-with-coordinates","title":"1. Pass class labels along with coordinates","text":"

So, if you have coordinates of three bounding boxes that look like this:

Python
bboxes = [\n    [23, 74, 295, 388],\n    [377, 294, 252, 161],\n    [333, 421, 49, 49],\n]\n

you can add a class label for each bounding box as an additional element of the list along with four coordinates. So now a list with bounding boxes and their coordinates will look the following:

Python
bboxes = [\n    [23, 74, 295, 388, 'dog'],\n    [377, 294, 252, 161, 'cat'],\n    [333, 421, 49, 49, 'sports ball'],\n]\n

or with multiple labels per each bounding box: Python

bboxes = [\n    [23, 74, 295, 388, 'dog', 'animal'],\n    [377, 294, 252, 161, 'cat', 'animal'],\n    [333, 421, 49, 49, 'sports ball', 'item'],\n]\n

You can use any data type for declaring class labels. It can be string, integer, or any other Python data type.

Next, you pass an image and bounding boxes for it to the transform function and receive the augmented image and bounding boxes.

Python
transformed = transform(image=image, bboxes=bboxes)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\n

Example input and output data for bounding boxes augmentation

"},{"location":"getting_started/bounding_boxes_augmentation/#2-pass-class-labels-in-a-separate-argument-to-transform-the-preferred-way","title":"2. Pass class labels in a separate argument to transform (the preferred way).","text":"

Let's say you have coordinates of three bounding boxes Python

bboxes = [\n    [23, 74, 295, 388],\n    [377, 294, 252, 161],\n    [333, 421, 49, 49],\n]\n

You can create a separate list that contains class labels for those bounding boxes:

Python
class_labels = ['cat', 'dog', 'parrot']\n

Then you pass both bounding boxes and class labels to transform. Note that to pass class labels, you need to use the name of the argument that you declared in label_fields when creating an instance of Compose in step 2. In our case, we set the name of the argument to class_labels.

Python
transformed = transform(image=image, bboxes=bboxes, class_labels=class_labels)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\ntransformed_class_labels = transformed['class_labels']\n

Example input and output data for bounding boxes augmentation with a separate argument for class labels

Note that label_fields expects a list, so you can set multiple fields that contain labels for your bounding boxes. So if you declare Compose like

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco', label_fields=['class_labels', 'class_categories'])))\n

you can use those multiple arguments to pass info about class labels, like

Python
class_labels = ['cat', 'dog', 'parrot']\nclass_categories = ['animal', 'animal', 'item']\n\ntransformed = transform(image=image, bboxes=bboxes, class_labels=class_labels, class_categories=class_categories)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\ntransformed_class_labels = transformed['class_labels']\ntransformed_class_categories = transformed['class_categories']\n
"},{"location":"getting_started/bounding_boxes_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/image_augmentation/","title":"Image augmentation for classification","text":"

We can divide the process of image augmentation into four steps:

  1. Import albumentations and a library to read images from the disk (e.g., OpenCV).
  2. Define an augmentation pipeline.
  3. Read images from the disk.
  4. Pass images to the augmentation pipeline and receive augmented images.
"},{"location":"getting_started/image_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"
  • Import Albumentations
Python
import albumentations as A\n
  • Import a library to read images from the disk. In this example, we will use OpenCV. It is an open-source computer vision library that supports many image formats. Albumentations has OpenCV as a dependency, so you already have OpenCV installed.
Python
import cv2\n
"},{"location":"getting_started/image_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

To define an augmentation pipeline, you need to create an instance of the Compose class. As an argument to the Compose class, you need to pass a list of augmentations you want to apply. A call to Compose will return a transform function that will perform image augmentation.

Let's look at an example:

Python
transform = A.Compose([\n    A.RandomCrop(width=256, height=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n

In the example, Compose receives a list with three augmentations: A.RandomCrop, A.HorizontalFlip, and A.RandomBrighntessContrast. You can find the full list of all available augmentations in the GitHub repository and in the API Docs. A demo playground that demonstrates how augmentations will transform the input image is available at https://explore.albumentations.ai.

To create an augmentation, you create an instance of the required augmentation class and pass augmentation parameters to it. A.RandomCrop receives two parameters, height and width. A.RandomCrop(width=256, height=256) means that A.RandomCrop will take an input image, extract a random patch with size 256 by 256 pixels from it and then pass the result to the next augmentation in the pipeline (in this case to A.HorizontalFlip).

A.HorizontalFlip in this example has one parameter named p. p is a special parameter that is supported by almost all augmentations. It controls the probability of applying the augmentation. p=0.5 means that with a probability of 50%, the transform will flip the image horizontally, and with a probability of 50%, the transform won't modify the input image.

A.RandomBrighntessContrast in the example also has one parameter, p. With a probability of 20%, this augmentation will change the brightness and contrast of the image received from A.HorizontalFlip. And with a probability of 80%, it will keep the received image unchanged.

A visualized version of the augmentation pipeline. You pass an image to it, the image goes through all transformations, and then you receive an augmented image from the pipeline.

"},{"location":"getting_started/image_augmentation/#step-3-read-images-from-the-disk","title":"Step 3. Read images from the disk.","text":"

To pass an image to the augmentation pipeline, you need to read it from the disk. The pipeline expects to receive an image in the form of a NumPy array. If it is a color image, it should have three channels in the following order: Red, Green, Blue (so a regular RGB image).

To read images from the disk, you can use OpenCV - a popular library for image processing. It supports a lot of input formats and is installed along with Albumentations since Albumentations utilizes that library under the hood for a lot of augmentations.

To import OpenCV

Python
import cv2\n

To read an image with OpenCV

Python

image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
Note the usage of cv2.cvtColor. For historical reasons, OpenCV reads an image in BGR format (so color channels of the image have the following order: Blue, Green, Red). Albumentations uses the most common and popular RGB image format. So when using OpenCV, we need to convert the image format to RGB explicitly.

Besides OpenCV, you can use other image processing libraries.

"},{"location":"getting_started/image_augmentation/#pillow","title":"Pillow","text":"

Pillow is a popular Python image processing library.

  • Install Pillow
Bash
    pip install pillow\n
  • Import Pillow and NumPy (we need NumPy to convert a Pillow image to a NumPy array. NumPy is already installed along with Albumentations).
Python
from PIL import Image\nimport numpy as np\n
  • Read an image with Pillow and convert it to a NumPy array. Python
    pillow_image = Image.open(\"image.jpg\")\nimage = np.array(pillow_image)\n
"},{"location":"getting_started/image_augmentation/#step-4-pass-images-to-the-augmentation-pipeline-and-receive-augmented-images","title":"Step 4. Pass images to the augmentation pipeline and receive augmented images.","text":"

To pass an image to the augmentation pipeline you need to call the transform function created by a call to A.Compose at Step 2. In the image argument to that function, you need to pass an image that you want to augment.

Python
transformed = transform(image=image)\n

transform will return a dictionary with a single key image. Value at that key will contain an augmented image.

Python
transformed_image = transformed[\"image\"]\n

To augment the next image, you need to call transform again and pass a new image as the image argument:

Python
another_transformed_image = transform(image=another_image)[\"image\"]\n

Each augmentation will change the input image with the probability set by the parameter p. Also, many augmentations have parameters that control the magnitude of changes that will be applied to an image. For example, A.RandomBrightnessContrast has two parameters: brightness_limit that controls the magnitude of adjusting brightness and contrast_limit that controls the magnitude of adjusting contrast. The bigger the value, the more the augmentation will change an image. During augmentation, a magnitude of the transformation is sampled from a uniform distribution limited by brightness_limit and contrast_limit. That means that if you make multiple calls to transform with the same input image, you will get a different output image each time.

Python
transform = A.Compose([\n    A.RandomBrightnessContrast(brightness_limit=1, contrast_limit=1, p=1.0),\n])\ntransformed_image_1 = transform(image=image)['image']\ntransformed_image_2 = transform(image=image)['image']\ntransformed_image_3 = transform(image=image)['image']\n

"},{"location":"getting_started/image_augmentation/#examples","title":"Examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Working with non-8-bit images
  • Weather augmentations in Albumentations
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/installation/","title":"Installation","text":"

Albumentations requires Python 3.8 or higher.

"},{"location":"getting_started/installation/#install-the-latest-stable-version-from-pypi","title":"Install the latest stable version from PyPI","text":"Bash
pip install -U albumentations\n
"},{"location":"getting_started/installation/#install-the-latest-version-from-the-master-branch-on-github","title":"Install the latest version from the master branch on GitHub","text":"Bash
pip install -U git+https://github.com/albumentations-team/albumentations\n
"},{"location":"getting_started/installation/#note-on-opencv-dependencies","title":"Note on OpenCV dependencies","text":"

By default, pip downloads a wheel distribution of Albumentations. This distribution has opencv-python-headless as its dependency.

If you already have some OpenCV distribution (such as opencv-python-headless, opencv-python, opencv-contrib-python or opencv-contrib-python-headless) installed in your Python environment, you can force Albumentations to use it by providing the --no-binary qudida,albumentations argument to pip, e.g.

Bash
pip install -U albumentations\n

pip will use the following logic to determine the required OpenCV distribution:

  1. If your Python environment already contains opencv-python, opencv-contrib-python, opencv-contrib-python-headless or opencv-python-headless pip will use it.
  2. If your Python environment doesn't contain any OpenCV distribution from step 1, pip will download opencv-python-headless.
"},{"location":"getting_started/installation/#install-the-latest-stable-version-from-conda-forge","title":"Install the latest stable version from conda-forge","text":"

If you are using Anaconda or Miniconda you can install Albumentations from conda-forge:

Bash
conda install -c conda-forge albumentations\n
"},{"location":"getting_started/keypoints_augmentation/","title":"Keypoints augmentation","text":"

Computer vision tasks such as human pose estimation, face detection, and emotion recognition usually work with keypoints on the image.

In the case of pose estimation, keypoints mark human joints such as shoulder, elbow, wrist, knee, etc.

Keypoints annotations along with visualized edges between keypoints. Images are from the COCO dataset.

In the case of face detection, keypoints mark important areas of the face such as eyes, nose, corners of the mouth, etc.

Facial keypoints. Source: the \"Facial Keypoints Detection\" competition on Kaggle.

To define a keypoint, you usually need two values, x and y coordinates of the keypoint. Coordinates of the keypoint are calculated with respect to the top-left corner of the image which has (x, y) coordinates (0, 0). Often keypoints have associated labels such as right_elbow, left_wrist, etc.

An example image with five keypoints from the COCO dataset

Some classical computer vision algorithms, such as SIFT, may use four values to describe a keypoint. In addition to the x and y coordinates, there are keypoint scale and keypoint angle. Albumentations support those values as well.

A keypoint may also has associated scale and angle values

Keypoint angles are counter-clockwise. For example, in the following image, the angle value is 65\u00b0. You can read more about angle of rotation in the Wikipedia article.

"},{"location":"getting_started/keypoints_augmentation/#supported-formats-for-keypoints-coordinates","title":"Supported formats for keypoints' coordinates.","text":"
  • xy. A keypoint is defined by x and y coordinates in pixels.

  • yx. A keypoint is defined by y and x coordinates in pixels.

  • xya. A keypoint is defined by x and y coordinates in pixels and the angle.

  • xys. A keypoint is defined by x and y coordinates in pixels, and the scale.

  • xyas. A keypoint is defined by x and y coordinates in pixels, the angle, and the scale.

  • xysa. A keypoint is defined by x and y coordinates in pixels, the scale, and the angle.

"},{"location":"getting_started/keypoints_augmentation/#augmenting-keypoints","title":"Augmenting keypoints","text":"

The process of augmenting keypoints looks very similar to the bounding boxes augmentation. It consists of 4 steps.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and keypoints from the disk.
  4. You pass an image and keypoints to the augmentation pipeline and receive augmented images and keypoints.

Note

Some transforms in Albumentation don't support keypoints. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment keypoints.

"},{"location":"getting_started/keypoints_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"Python
import albumentations as A\nimport cv2\n
"},{"location":"getting_started/keypoints_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

Here an example of a minimal declaration of an augmentation pipeline that works with keypoints.

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy'))\n

Note that just like with bounding boxes, Compose has an additional parameter that defines the format for keypoints' coordinates. In the case of keypoints, it is called keypoint_params. Here we pass an instance of A.KeypointParams that says that xy coordinates format should be used.

Besides format, A.KeypointParams supports a few more settings.

Here is an example of Compose that shows all available settings with A.KeypointParams

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels'], remove_invisible=True, angle_in_degrees=True))\n
"},{"location":"getting_started/keypoints_augmentation/#label_fields","title":"label_fields","text":"

In some computer vision tasks, keypoints have not only coordinates but associated labels as well. For example, in pose estimation, each keypoint has a label such as elbow, knee or wrist. You need to pass those labels in a separate argument (or arguments, because you can use multiple fields) to the transform function that will augment keypoints. label_fields defines names of those fields. Step 4 describes how you need to use the transform function.

"},{"location":"getting_started/keypoints_augmentation/#remove_invisible","title":"remove_invisible","text":"

After the augmentation, some keypoints may become invisible because they will be located outside of the augmented image's visible area. For example, if you crop a part of the image, all the keypoints outside of the cropped area will become invisible. If remove_invisible is set to True, Albumentations won't return invisible keypoints. remove_invisible is set to True by default, so if you don't pass that argument, Albumentations won't return invisible keypoints.

"},{"location":"getting_started/keypoints_augmentation/#angle_in_degrees","title":"angle_in_degrees","text":"

If angle_in_degrees is set to True (this is the default value), then Albumentations expects that the angle value in formats xya, xyas, and xysa is defined in angles. If angle_in_degrees is set to False, Albumentations expects that the angle value is specified in radians.

This setting doesn't affect xy and yx formats, because those formats don't use angles.

"},{"location":"getting_started/keypoints_augmentation/#3-read-images-and-keypoints-from-the-disk","title":"3. Read images and keypoints from the disk.","text":"

Read an image from the disk.

Python

image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
Keypoints can be stored on the disk in different serialization formats: JSON, XML, YAML, CSV, etc. So the code to read keypoints depends on the actual format of data on the disk.

After you read the data from the disk, you need to prepare keypoints for Albumentations.

Albumentations expects that keypoint will be represented as a list of lists. Each list contains information about a single keypoint. A definition of keypoint should have two to four elements depending on the selected format of keypoints. The first two elements are x and y coordinates of a keypoint in pixels (or y and x coordinates in the yx format). The third and fourth elements may be the angle and the scale of keypoint if you select a format that uses those values.

"},{"location":"getting_started/keypoints_augmentation/#step-4-pass-an-image-and-keypoints-to-the-augmentation-pipeline-and-receive-augmented-images-and-boxes","title":"Step 4. Pass an image and keypoints to the augmentation pipeline and receive augmented images and boxes.","text":"

Let's say you have an example image with five keypoints.

A list with those five keypoints' coordinates in the xy format will look the following:

Python
keypoints = [\n    (264, 203),\n    (86, 88),\n    (254, 160),\n    (193, 103),\n    (65, 341),\n]\n

Then you pass those keypoints to the transform function along with the image and receive the augmented versions of image and keypoints.

Python
transformed = transform(image=image, keypoints=keypoints)\ntransformed_image = transformed['image']\ntransformed_keypoints = transformed['keypoints']\n

The augmented image with augmented keypoints

If you set remove_invisible to False in keypoint_params, then Albumentations will return all keypoints, even if they lie outside the visible area. In the example image below, you can see that the keypoint for the right hip is located outside the image, but Albumentations still returned it. The area outside the image is highlighted in yellow.

When remove_invisible is set to False Albumentations will return all keypoints, even those located outside the image

If keypoints have associated class labels, you need to create a list that contains those labels:

Python
class_labels = [\n    'left_elbow',\n    'right_elbow',\n    'left_wrist',\n    'right_wrist',\n    'right_hip',\n]\n

Also, you need to declare the name of the argument to transform that will contain those labels. For declaration, you need to use the label_fields parameters of A.KeypointParams.

For example, we could use the class_labels name for the argument with labels.

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels']))\n

Next, you pass both keypoints' coordinates and class labels to transform.

Python
transformed = transform(image=image, keypoints=keypoints, class_labels=class_labels)\ntransformed_image = transformed['image']\ntransformed_keypoints = transformed['keypoints']\ntransformed_class_labels = transformed['class_labels']\n

Note that label_fields expects a list, so you can set multiple fields that contain labels for your keypoints. So if you declare Compose like

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels', 'class_sides']))\n

you can use those multiple arguments to pass info about class labels, like

Python
class_labels = [\n    'left_elbow',\n    'right_elbow',\n    'left_wrist',\n    'right_wrist',\n    'right_hip',\n]\n\nclass_sides = ['left', 'right', 'left', 'right', 'right']\n\ntransformed = transform(image=image, keypoints=keypoints, class_labels=class_labels, class_sides=class_sides)\ntransformed_class_sides = transformed['class_sides']\ntransformed_class_labels = transformed['class_labels']\ntransformed_keypoints = transformed['keypoints']\ntransformed_image = transformed['image']\n

Example input and output data for keypoints augmentation with two separate arguments for class labels

Note

Some augmentations may affect class labels and make them incorrect. For example, the HorizontalFlip augmentation mirrors the input image. When you apply that augmentation to keypoints that mark the side of body parts (left or right), those keypoints will point to the wrong side (since left on the mirrored image becomes right). So when you are creating an augmentation pipeline look carefully which augmentations could be applied to the input data.

HorizontalFlip may make keypoints' labels incorrect

"},{"location":"getting_started/keypoints_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations to augment keypoints
"},{"location":"getting_started/mask_augmentation/","title":"Mask augmentation for segmentation","text":"

For instance and semantic segmentation tasks, you need to augment both the input image and one or more output masks.

Albumentations ensures that the input image and the output mask will receive the same set of augmentations with the same parameters.

The process of augmenting images and masks looks very similar to the regular image-only augmentation.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and masks from the disk.
  4. You pass an image and one or more masks to the augmentation pipeline and receive augmented images and masks.
"},{"location":"getting_started/mask_augmentation/#steps-1-and-2-import-the-required-libraries-and-define-an-augmentation-pipeline","title":"Steps 1 and 2. Import the required libraries and define an augmentation pipeline.","text":"

Image augmentation for classification described Steps 1 and 2 in great detail. These are the same steps for the simultaneous augmentation of images and masks.

Python
import albumentations as A\nimport cv2\n\ntransform = A.Compose([\n    A.RandomCrop(width=256, height=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n
"},{"location":"getting_started/mask_augmentation/#step-3-read-images-and-masks-from-the-disk","title":"Step 3. Read images and masks from the disk.","text":"
  • Reading an image
Python
image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
  • For semantic segmentation, you usually read one mask per image. Albumentations expects the mask to be a NumPy array. The height and width of the mask should have the same values as the height and width of the image.
Python
mask = cv2.imread(\"/path/to/mask.png\")\n
  • For instance segmentation, you sometimes need to read multiple masks per image. Then you create a list that contains all the masks.
Python
mask_1 = cv2.imread(\"/path/to/mask_1.png\")\nmask_2 = cv2.imread(\"/path/to/mask_2.png\")\nmask_3 = cv2.imread(\"/path/to/mask_3.png\")\nmasks = [mask_1, mask_2, mask_3]\n

Some datasets use other formats to store masks. For example, they can use Run-Length Encoding or Polygon coordinates. In that case, you need to convert a mask to a NumPy before augmenting it with Albumentations. Often dataset authors provide special libraries and tools to simplify the conversion.

"},{"location":"getting_started/mask_augmentation/#step-4-pass-image-and-masks-to-the-augmentation-pipeline-and-receive-augmented-images-and-masks","title":"Step 4. Pass image and masks to the augmentation pipeline and receive augmented images and masks.","text":"

If the image has one associated mask, you need to call transform with two arguments: image and mask. In image you should pass the input image, in mask you should pass the output mask. transform will return a dictionary with two keys: image will contain the augmented image, and mask will contain the augmented mask.

Python
transformed = transform(image=image, mask=mask)\ntransformed_image = transformed['image']\ntransformed_mask = transformed['mask']\n

An image and a mask before and after augmentation. Inria Aerial Image Labeling dataset contains aerial photos as well as their segmentation masks. Each pixel of the mask is marked as 1 if the pixel belongs to the class building and 0 otherwise.

If the image has multiple associated masks, you should use the masks argument instead of mask. In masks you should pass a list of masks.

Python
transformed = transform(image=image, masks=masks)\ntransformed_image = transformed['image']\ntransformed_masks = transformed['masks']\n
"},{"location":"getting_started/mask_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations for a semantic segmentation task
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/setting_probabilities/","title":"Setting probabilities for transforms in an augmentation pipeline","text":"

Each augmentation in Albumentations has a parameter named p that sets the probability of applying that augmentation to input data.

The following augmentations have the default value of p set 1 (which means that by default they will be applied to each instance of input data): Compose, ReplayCompose, CenterCrop, Crop, CropNonEmptyMaskIfExists, FromFloat, CenterCrop, Crop, CropNonEmptyMaskIfExists, FromFloat, IAACropAndPad, Lambda, LongestMaxSize, Normalize, PadIfNeeded, RandomCrop, RandomCropNearBBox, RandomResizedCrop, RandomSizedBBoxSafeCrop, RandomSizedCrop, Resize, SmallestMaxSize, ToFloat.

All other augmentations have the default value of p set 0.5, which means that by default, they will be applied to 50% of instances of input data.

Let's take a look at the example:

Python
import albumentations as A\nimport cv2\n\np1 = 0.95\np2 = 0.85\np3 = 0.75\n\n\ntransform = A.Compose([\n    A.RandomRotate90(p=p2),\n    A.OneOf([\n        A.IAAAdditiveGaussianNoise(p=0.9),\n        A.GaussNoise(p=0.6),\n    ], p=p3)\n], p=p1)\n\nimage = cv2.imread('some/image.jpg')\nimage = cv2.cvtColor(cv2.COLOR_BGR2RGB)\n\ntransformed = transform(image=image)\ntransformed_image = transformed['image']\n

We declare an augmentation pipeline. In this pipeline, we use three placeholder values to set probabilities: p1, p2, and p3. Let's take a closer look at them.

"},{"location":"getting_started/setting_probabilities/#p1","title":"p1","text":"

p1 sets the probability that the augmentation pipeline will apply augmentations at all.

If p1 is set to 0, then augmentations inside Compose will never be applied to the input image, so the augmentation pipeline will always return the input image unchanged.

If p1 is set to 1, then all augmentations inside Compose will have a chance to be applied. The example above contains two augmentations inside Compose: RandomRotate90 and the OneOf block with two child augmentations (more on their probabilities later). Any value of p1 between 0 and 1 means that augmentations inside Compose could be applied with the probability between 0 and 100%.

If p1 equals to 1 or p1 is less than 1, but the random generator decides to apply augmentations inside Compose probabilities p2 and p3 come into play.

"},{"location":"getting_started/setting_probabilities/#p2","title":"p2","text":"

Each augmentation inside Compose has a probability of being applied. p2 sets the probability of applying RandomRotate90. In the example above, p2 equals 0.85, so RandomRotate90 has an 85% chance to be applied to the input image.

"},{"location":"getting_started/setting_probabilities/#p3","title":"p3","text":"

p3 sets the probability of applying the OneOf block. If the random generator decided to apply RandomRotate90 at the previous step, then OneOf will receive data augmented by it. If the random generator decided not to apply RandomRotate90 then OneOf will receive the input data (that was passed to Compose) since RandomRotate90 is skipped.

The OneOfblock applies one of the augmentations inside it. That means that if the random generator chooses to apply OneOf then one child augmentation from it will be applied to the input data.

To decide which augmentation within the OneOf block is used, Albumentations uses the following rule:

The OneOf block normalizes the probabilities of all augmentations inside it, so their probabilities sum up to 1. Next, OneOf chooses one of the augmentations inside it with a chance defined by its normalized probability and applies it to the input data. In the example above IAAAdditiveGaussianNoise has probability 0.9 and GaussNoise probability 0.6. After normalization, they become 0.6 and 0.4. Which means that OneOf will decide that it should use IAAAdditiveGaussianNoise with probability 0.6 and GaussNoise otherwise.

"},{"location":"getting_started/setting_probabilities/#example-calculations","title":"Example calculations","text":"

Thus, each augmentation in the example above will be applied with the probability:

  • RandomRotate90: p1 * p2
  • IAAAdditiveGaussianNoise: p1 * p3 * (0.9 / (0.9 + 0.6))
  • GaussianNoise: p1 * p3 * (0.6 / (0.9 + 0.6))
"},{"location":"getting_started/simultaneous_augmentation/","title":"Simultaneous augmentation of multiple targets: masks, bounding boxes, keypoints","text":"

Albumentations can apply the same set of transformations to the input images and all the targets that are passed to transform: masks, bounding boxes, and keypoints.

Please refer to articles Image augmentation for classification, Mask augmentation for segmentation, Bounding boxes augmentation for object detection, and Keypoints augmentation for the detailed description of each data type.

Note

Some transforms in Albumentation don't support bounding boxes or keypoints. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment bounding boxes and keypoints.

Below is an example, how you can simultaneously augment the input image, mask, bounding boxes with their labels, and keypoints with their labels. Note that the only required argument to transform is image; all other arguments are optional, and you can combine them in any way.

"},{"location":"getting_started/simultaneous_augmentation/#step-1-define-compose-with-parameters-that-specify-formats-for-bounding-boxes-and-keypoints","title":"Step 1. Define Compose with parameters that specify formats for bounding boxes and keypoints.","text":"Python
transform = A.Compose(\n  [A.RandomCrop(width=330, height=330), A.RandomBrightnessContrast(p=0.2)],\n  bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"bbox_classes\"]),\n  keypoint_params=A.KeypointParams(format=\"xy\", label_fields=[\"keypoints_classes\"]),\n)\n
"},{"location":"getting_started/simultaneous_augmentation/#step-2-load-all-required-data-from-the-disk","title":"Step 2. Load all required data from the disk","text":"

Please refer to articles Image augmentation for classification, Mask augmentation for segmentation, Bounding boxes augmentation for object detection, and Keypoints augmentation for more information about loading the input data.

For example, here is an image from the COCO dataset. that has one associated mask, one bounding box with the class label person, and five keypoints that define body parts.

An example image with mask, bounding boxes and keypoints

"},{"location":"getting_started/simultaneous_augmentation/#step-3-pass-all-targets-to-transform-and-receive-their-augmented-versions","title":"Step 3. Pass all targets to transform and receive their augmented versions","text":"Python
transformed = transform(\n  image=img,\n  mask=mask,\n  bboxes=bboxes,\n  bbox_classes=bbox_classes,\n  keypoints=keypoints,\n  keypoints_classes=keypoints_classes,\n)\ntransformed_image = transformed[\"image\"]\ntransformed_mask = transformed[\"mask\"]\ntransformed_bboxes = transformed[\"bboxes\"]\ntransformed_bbox_classes = transformed[\"bbox_classes\"]\ntransformed_keypoints = transformed[\"keypoints\"]\ntransformed_keypoints_classes = transformed[\"keypoints_classes\"]\n

The augmented version of the image and its targets

"},{"location":"getting_started/simultaneous_augmentation/#examples","title":"Examples","text":"
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/transforms_and_targets/","title":"A list of transforms and their supported targets","text":"

We can split all transforms into two groups: pixel-level transforms, and spatial-level transforms. Pixel-level transforms will change just an input image and will leave any additional targets such as masks, bounding boxes, and keypoints unchanged. Spatial-level transforms will simultaneously change both an input image as well as additional targets such as masks, bounding boxes, and keypoints. For the additional information, please refer to this section of \"Why you need a dedicated library for image augmentation\".

"},{"location":"getting_started/transforms_and_targets/#pixel-level-transforms","title":"Pixel-level transforms","text":"

Here is a list of all available pixel-level transforms. You can apply a pixel-level transform to any target, and under the hood, the transform will change only the input image and return any other input targets such as masks, bounding boxes, or keypoints unchanged.

  • AdditiveNoise
  • AdvancedBlur
  • AutoContrast
  • Blur
  • CLAHE
  • ChannelDropout
  • ChannelShuffle
  • ChromaticAberration
  • ColorJitter
  • Defocus
  • Downscale
  • Emboss
  • Equalize
  • FDA
  • FancyPCA
  • FromFloat
  • GaussNoise
  • GaussianBlur
  • GlassBlur
  • HistogramMatching
  • HueSaturationValue
  • ISONoise
  • Illumination
  • ImageCompression
  • InvertImg
  • MedianBlur
  • MotionBlur
  • MultiplicativeNoise
  • Normalize
  • PixelDistributionAdaptation
  • PlanckianJitter
  • PlasmaBrightnessContrast
  • PlasmaShadow
  • Posterize
  • RGBShift
  • RandomBrightnessContrast
  • RandomFog
  • RandomGamma
  • RandomGravel
  • RandomRain
  • RandomShadow
  • RandomSnow
  • RandomSunFlare
  • RandomToneCurve
  • RingingOvershoot
  • SaltAndPepper
  • Sharpen
  • ShotNoise
  • Solarize
  • Spatter
  • Superpixels
  • TemplateTransform
  • TextImage
  • ToFloat
  • ToGray
  • ToRGB
  • ToSepia
  • UnsharpMask
  • ZoomBlur
"},{"location":"getting_started/transforms_and_targets/#spatial-level-transforms","title":"Spatial-level transforms","text":"

Here is a table with spatial-level transforms and targets they support. If you try to apply a spatial-level transform to an unsupported target, Albumentations will raise an error.

Transform Image Mask BBoxes Keypoints Volume Mask3D Affine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 AtLeastOneBBoxRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 BBoxSafeRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CenterCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CoarseDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Crop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropAndPad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropNonEmptyMaskIfExists \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 D4 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ElasticTransform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Erasing \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 FrequencyMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridElasticDeform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 HorizontalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Lambda \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 LongestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 MaskDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Morphological \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 NoOp \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OpticalDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OverlayElements \u2713 \u2713 Pad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PadIfNeeded \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Perspective \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PiecewiseAffine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PixelDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropFromBorders \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropNearBBox \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomGridShuffle \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomResizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomRotate90 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomScale \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedBBoxSafeCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Resize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Rotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SafeRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ShiftScaleRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SmallestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ThinPlateSpline \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeReverse \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Transpose \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 VerticalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 XYMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713"},{"location":"getting_started/video_augmentation/","title":"Working with Video Data in Albumentations","text":""},{"location":"getting_started/video_augmentation/#overview","title":"Overview","text":"

While Albumentations is primarily known for image augmentation, it can effectively process video data by treating it as a sequence of frames. When you pass a video as a numpy array, Albumentations will apply the same transform with identical parameters to each frame, ensuring temporal consistency.

"},{"location":"getting_started/video_augmentation/#data-format","title":"Data Format","text":""},{"location":"getting_started/video_augmentation/#video-frames","title":"Video Frames","text":"

Albumentations accepts video data as numpy arrays in the following formats: - (N, H, W) - Grayscale video (N frames) - (N, H, W, C) - Color video (N frames)

Where: - N = Number of frames - H = Height - W = Width - C = Channels (e.g., 3 for RGB)

"},{"location":"getting_started/video_augmentation/#video-masks","title":"Video Masks","text":"

For video segmentation tasks, masks should match the frame dimensions: - (N, H, W) - Binary or single-class masks - (N, H, W, C) - Multi-class masks

"},{"location":"getting_started/video_augmentation/#basic-usage","title":"Basic Usage","text":"Python
import albumentations as A\nimport numpy as np\n
"},{"location":"getting_started/video_augmentation/#create-transform-pipeline","title":"Create transform pipeline","text":"Python
transform = A.Compose([\n    A.RandomCrop(height=224, width=224),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], seed=42)\n
"},{"location":"getting_started/video_augmentation/#example-video-data","title":"Example video data","text":"Python
video = np.random.rand(32, 256, 256, 3) # 32 RGB frames\nmasks = np.zeros((32, 256, 256)) # 32 binary masks\n
"},{"location":"getting_started/video_augmentation/#apply-transform","title":"Apply transform","text":"Python
augmented_video = transform(images=video, masks=masks)\n
"},{"location":"getting_started/video_augmentation/#apply-transforms-same-parameters-for-all-frames","title":"Apply transforms - same parameters for all frames","text":"Python
transformed = transform(images=video, mask=masks)\ntransformed_video = transformed['image']\ntransformed_masks = transformed['mask']\n
"},{"location":"getting_started/video_augmentation/#key-features","title":"Key Features","text":"
  1. Temporal Consistency: The same transform with identical parameters is applied to all frames, preserving temporal consistency.

  2. Memory Efficiency: Frames are processed as a batch, avoiding repeated parameter generation.

  3. Compatible with All Transforms: Works with any Albumentations transform that supports the image target.

"},{"location":"getting_started/video_augmentation/#example-pipeline-for-video-processing","title":"Example Pipeline for Video Processing","text":"Python
def create_video_pipeline(\n    crop_size=(224, 224),\n    p_spatial=0.5,\n    p_color=0.3\n    ):\n    return A.Compose([\n        # Spatial transforms - same crop/flip for all frames\n        A.RandomCrop(\n            height=crop_size[0],\n            width=crop_size[1],\n            p=1.0\n        ),\n        A.HorizontalFlip(p=p_spatial),\n        # Color transforms - same adjustment for all frames\n        A.ColorJitter(\n            brightness=0.2,\n            contrast=0.2,\n            saturation=0.2,\n            hue=0.1,\n            p=p_color\n        ),\n        # Noise/blur - same pattern for all frames\n        A.GaussianBlur(p=0.3),\n    ])\n
"},{"location":"getting_started/video_augmentation/#best-practices","title":"Best Practices","text":"
  1. Performance Optimization:
  2. Place cropping operations first to reduce computation
  3. Consider frame rate and whether all frames need processing
"},{"location":"getting_started/video_augmentation/#next-steps","title":"Next Steps","text":"
  • Learn about Volumetric Data (3D) for volumetric data
"},{"location":"getting_started/volumetric_augmentation/","title":"Introduction to 3D Medical Image Augmentation","text":""},{"location":"getting_started/volumetric_augmentation/#overview","title":"Overview","text":"

While primarily used for medical imaging (CT scans, MRI), Albumentations' 3D transforms can be applied to various volumetric data types

"},{"location":"getting_started/volumetric_augmentation/#medical-imaging","title":"Medical Imaging","text":"
  • CT and MRI scans
  • Ultrasound volumes
  • PET scans
  • Multi-modal medical imaging
"},{"location":"getting_started/volumetric_augmentation/#scientific-data","title":"Scientific Data","text":"
  • Microscopy z-stacks
  • Cryo-EM volumes
  • Geological seismic data
  • Weather radar volumes
"},{"location":"getting_started/volumetric_augmentation/#industrial-applications","title":"Industrial Applications","text":"
  • 3D NDT (Non-Destructive Testing) scans
  • Industrial CT for quality control
  • Material analysis volumes
  • 3D ultrasonic testing data
"},{"location":"getting_started/volumetric_augmentation/#computer-vision","title":"Computer Vision","text":"
  • Depth camera sequences
  • LiDAR point cloud voxelizations
  • Multi-view stereo reconstructions
"},{"location":"getting_started/volumetric_augmentation/#data-format","title":"Data Format","text":""},{"location":"getting_started/volumetric_augmentation/#volumes","title":"Volumes","text":"

Albumentations expects 3D volumes as numpy arrays in the following formats: - (D, H, W) - Single-channel volumes (e.g., CT scans) - (D, H, W, C) - Multi-channel volumes (e.g., multi-modal MRI)

Where: - D = Depth (number of slices) - H = Height - W = Width - C = Channels (optional)

"},{"location":"getting_started/volumetric_augmentation/#3d-masks","title":"3D Masks","text":"

Segmentation masks should match the volume dimensions: - (D, H, W) - Binary or single-class masks - (D, H, W, C) - Multi-class masks

"},{"location":"getting_started/volumetric_augmentation/#basic-usage","title":"Basic Usage","text":"Python
import albumentations as A\nimport numpy as np\n
"},{"location":"getting_started/volumetric_augmentation/#create-a-basic-3d-augmentation-pipeline","title":"Create a basic 3D augmentation pipeline","text":"Python
transform = A.Compose([\n    # Crop volume to a fixed size for memory efficiency\n    A.RandomCrop3D(size=(64, 128, 128), p=1.0),    \n    # Randomly remove cubic regions to simulate occlusions\n    A.CoarseDropout3D(\n        num_holes_range=(2, 6),\n        hole_depth_range=(0.1, 0.3),\n        hole_height_range=(0.1, 0.3),\n        hole_width_range=(0.1, 0.3),\n        p=0.5\n    ),    \n])\n
"},{"location":"getting_started/volumetric_augmentation/#apply-to-volume-and-mask","title":"Apply to volume and mask","text":"Python
volume = np.random.rand(96, 256, 256) # Your 3D medical volume\nmask = np.zeros((96, 256, 256)) # Your 3D segmentation mask\ntransformed = transform(volume=volume, mask3d=mask)\ntransformed_volume = transformed['volume']\ntransformed_mask = transformed['mask3d']\n
"},{"location":"getting_started/volumetric_augmentation/#available-3d-transforms","title":"Available 3D Transforms","text":"

Here are some examples of available 3D transforms:

  • CenterCrop3D - Crop the center part of a 3D volume
  • RandomCrop3D - Randomly crop a part of a 3D volume
  • Pad3D - Pad a 3D volume
  • PadIfNeeded3D - Pad if volume size is less than desired size
  • CoarseDropout3D - Random dropout of 3D cubic regions
  • CubicSymmetry - Apply random cubic symmetry transformations

For a complete and up-to-date list of all available 3D transforms, please see our API Reference.

"},{"location":"getting_started/volumetric_augmentation/#combining-2d-and-3d-transforms","title":"Combining 2D and 3D Transforms","text":"

You can combine 2D and 3D transforms in the same pipeline. 2D transforms will be applied slice-by-slice in the XY plane:

Python
transform = A.Compose([\n    # 3D transforms\n    A.RandomCrop3D(size=(64, 128, 128)),\n    # 2D transforms (applied to each XY slice)\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n\ntransformed = transform(volume=volume, mask3d=mask)\ntransformed_volume = transformed['volume']\ntransformed_mask = transformed['mask3d']\n
"},{"location":"getting_started/volumetric_augmentation/#best-practices","title":"Best Practices","text":"
  1. Memory Management: 3D volumes can be large. Consider using smaller crop sizes or processing in patches.
  2. Place cropping operations at the beginning of your pipeline for better performance
  3. Example: A 256x256x256 volume cropped to 64x64x64 will process subsequent transforms ~64x faster
"},{"location":"getting_started/volumetric_augmentation/#efficient-pipeline-cropping-first","title":"Efficient pipeline - cropping first","text":"Python
efficient_transform = A.Compose([\nA.RandomCrop3D(size=(64, 64, 64)), # Do this first!\nA.CoarseDropout3D(...),\nA.RandomBrightnessContrast(...)\n])\n
"},{"location":"getting_started/volumetric_augmentation/#less-efficient-pipeline-processing-full-volume-unnecessarily","title":"Less efficient pipeline - processing full volume unnecessarily","text":"Python
inefficient_transform = A.Compose([\nA.CoarseDropout3D(...), # Processing full volume\nA.RandomBrightnessContrast(...), # Processing full volume\nA.RandomCrop3D(size=(64, 64, 64)) # Cropping at the end\n])\n
  1. Avoid Interpolation Artifacts: For highest quality augmentation, prefer transforms that only rearrange existing voxels without interpolation:

a) Available Artifact-Free Transforms: - HorizontalFlip, VerticalFlip - Mirror images across X or Y axes - RandomRotate90 - Rotate by 90 degrees in XY plane - D4 - All possible combinations of flips and 90-degree rotations in XY plane (8 variants) - CubicSymmetry - 3D extension of D4, includes all 48 possible cube symmetries

These transforms maintain perfect image quality because they only move existing voxels to new positions without creating new values through interpolation.

b) Benefits of Artifact-Free Transforms:\n- Preserve original voxel values exactly\n- Maintain spatial relationships between tissues\n- No blurring or information loss\n- Faster computation (no interpolation needed)\n
"},{"location":"getting_started/volumetric_augmentation/#example-pipeline","title":"Example Pipeline","text":"

Here's a complete example of a medical image augmentation pipeline:

Python
import albumentations as A\nimport numpy as np\n\ndef create_3d_pipeline(\n    crop_size=(64, 128, 128),\n    p_spatial=0.5,\n    p_intensity=0.3\n    ):\n    return A.Compose([\n        # Spatial transforms\n        A.RandomCrop3D(\n            size=crop_size,\n            p=1.0\n        ),\n        A.CubicSymmetry(p=p_spatial),\n        # Intensity transforms\n        A.CoarseDropout3D(\n            num_holes_range=(2, 5),\n            hole_depth_range=(0.1, 0.2),\n            hole_height_range=(0.1, 0.2),\n            hole_width_range=(0.1, 0.2),\n            p=p_intensity\n        ),\n    ])\n
"},{"location":"getting_started/volumetric_augmentation/#usage","title":"Usage","text":"Python
transform = create_3d_pipeline()\nvolume = np.random.rand(96, 256, 256)\nmask = np.zeros((96, 256, 256))\ntransformed = transform(volume=volume, mask3d=mask)\n
"},{"location":"getting_started/volumetric_augmentation/#next-steps","title":"Next Steps","text":"
  • Learn about Video Augmentation for sequential data
"},{"location":"integrations/","title":"Integrations","text":"

Here are some examples of how to use Albumentations with different deep learning frameworks and tools:

  • HuggingFace
  • FiftyOne
  • Roboflow
"},{"location":"integrations/fiftyone/","title":"FiftyOne integration","text":""},{"location":"integrations/fiftyone/#introduction","title":"Introduction","text":"

FiftyOne is an open-source visualization and analysis tool for machine learning datasets, particularly useful in computer vision projects. It facilitates detailed dataset examination and the fine-tuning of model performance.

Albumentations could be used in FiftyOne via the FiftyOne Plugin.

With the FiftyOne Albumentations plugin, you can transform any and all labels of type Detections, Keypoints, Segmentation, and Heatmap, or just the images themselves.

Info

This tutorial is almost entirely based on the FiftyOne Documentation and serves as an overview of the functionality of the FiftyOne Albumentations plugin.

For more up to date information check the original source.

This integration guide will focus on the setup process and the functionality of the plugin.

For a tutorial on how to curate your augmentations, check out the Data Augmentation Tutorial as FiftyOne Documentation.

"},{"location":"integrations/fiftyone/#overview","title":"Overview","text":"

Albumentations supports 80+ transforms spanning pixel-level, geometric transformations, and more.

As of April 29, 2024 FiftyOne supports:

  • AdvancedBlur
  • GridDropout
  • MaskDropout
  • PiecewiseAffine
  • RandomGravel
  • RandomGridShuffle
  • RandomShadow
  • RandomSunFlare
  • Rotate
"},{"location":"integrations/fiftyone/#functionality","title":"Functionality","text":"

The FiftyOne Albumentations plugin provides the following functionality:

  • Apply Albumentations transformations to your dataset, your current view, or selected samples
  • Visualize the effects of these transformations directly within the FiftyOne App
  • View samples generated by the last applied transformation
  • Save augmented samples to the dataset
  • Get info about the last applied transformation
  • Save transformation pipelines to the dataset for reproducibility
"},{"location":"integrations/fiftyone/#setup","title":"Setup","text":"

Make sure you have FiftyOne and Albumentations installed:

Bash
pip install -U fiftyone albumentations\n

Next, install the FiftyOne Albumentations plugin:

Bash
fiftyone plugins download https://github.com/jacobmarks/fiftyone-albumentations-plugin\n

Note

If you have the FiftyOne Plugin Utils plugin installed, you can also install the Albumentations plugin via the install_plugin operator, selecting the Albumentations plugin from the community dropdown menu.

You will also need to load (and download if necessary) a dataset to apply the augmentations to. For this guide, we'll use the the quickstart dataset:

Python
import fiftyone as fo\nimport fiftyone.zoo as foz\n\n## only take 5 samples for quick demonstration\ndataset = foz.load_zoo_dataset(\"quickstart\", max_samples=5)\n\n# only keep the ground truth labels\ndataset.select_fields(\"ground_truth\").keep_fields()\n\nsession = fo.launch_app(dataset)\n

Note

The quickstart dataset only contains Detections labels. If you want to test Albumentations transformations on other label types, here are some quick examples to get you started, using FiftyOne's Hugging Face Transformers and Ultralytics integrations: Bash

pip install -U transformers ultralytics\n
Python
import fiftyone as fo\nimport fiftyone.zoo as foz\n\nfrom ultralytics import YOLO\n\n# Keypoints\nmodel = YOLO(\"yolov8l-pose.pt\")\ndataset.apply_model(model, label_field=\"keypoints\")\n\n# Instance Segmentation\nmodel = YOLO(\"yolov8l-seg.pt\")\ndataset.apply_model(model, label_field=\"instances\")\n\n# Semantic Segmentation\nmodel = foz.load_zoo_model(\n    \"segmentation-transformer-torch\",\n    name_or_path=\"Intel/dpt-large-ade\",\n)\ndataset.apply_model(model, label_field=\"mask\")\n\n# Heatmap\nmodel = foz.load_zoo_model(\n    \"depth-estimation-transformer-torch\",\n    name_or_path=\"LiheYoung/depth-anything-small-hf\",\n)\ndataset.apply_model(model, label_field=\"depth_map\")\n

"},{"location":"integrations/fiftyone/#apply-transformations","title":"Apply transformations","text":"

To apply Albumentations transformations to your dataset, you can use the augment_with_albumentations operator. Press the backtick key to open the operator modal, and select the augment_with_albumentations operator from the dropdown menu.

You can then configure the transformations to apply:

  • Number of augmentations per sample: The number of augmented samples to generate for each input sample. The default is 1, which is sufficient for deterministic transformations, but for probabilistic transformations, you may want to generate multiple samples to see the range of possible outputs.
  • Number of transforms: The number of transformations to compose into the pipeline to be applied to each sample. The default is 1, but you can set this as high as you'd like \u2014 the more transformations, the more complex the augmentations will be. You will be able to configure each transform separately.
  • Target view: The view to which the transformations will be applied. The default is dataset, but you can also apply the transformations to the current view or to currently selected samples within the app.
  • Execution mode: If you set delegated=False, the operation will be executed immediately. If you set delegated=True, the operation will be queued as a job, which you can then run in the background from your terminal with:
Bash
fiftyone delegated launch\n

For each transformation, you can select either a \"primitive\" transformation from the Albumentations library, or a \"saved\" transformation pipeline that you have previously saved to the dataset. These saved pipelines can consist of one or more transformations.

When you apply a primitive transformation, you can configure the parameters of the transformation directly within the app. The available parameters, their default values, types, and docstrings are all integrated directly from the Albumentations library.

When you apply a saved pipeline, there will not be any parameters to configure.

"},{"location":"integrations/fiftyone/#visualize-transformations","title":"Visualize transformations","text":"

Once you've applied the transformations, you can visualize the effects of the transformations directly within the FiftyOne App. All augmented samples will be added to the dataset, and will be tagged as augmented so that you can easily filter for just augmented or non-augmented samples in the app.

You can also filter for augmented samples programmatically with the match_tags() method:

Python
# get just the augmented samples\naugmented_view = dataset.match_tags(\"augmented\")\n\n# get just the non-augmented samples\nnon_augmented_view = dataset.match_tags(\"augmented\", bool=False)\n

However, matching on these tags will return all samples that have been generated by an augmentation, not just the samples that were generated by the last applied transformation \u2014 as you will see shortly, we can save augmentations to the dataset. To get just the samples generated by the last applied transformation, you can use the view_last_albumentations_run operator:

Note

For all samples added to the dataset by the FiftyOne Albumentations plugin, there will be a field \"transform\", which contains the information not just about the pipeline that was applied, but also about the specific parameters that were used for this application of the pipeline. For example, if you had a HorizontalFlip transformation with an application probability of p=0.5, the contents of the \"transform\" field tell you whether or not this transformation was applied to the sample!

"},{"location":"integrations/fiftyone/#save-augmentations","title":"Save augmentations","text":"

By default all augmentations are temporary, as the FiftyOne Albumentations plugin is primarily designed for rapid prototyping and experimentation. This means that when you generated a new batch of augmented samples, the previous batch of augmented samples will be removed from the dataset, and the image files will be deleted from disk.

However, if you want to save the augmented samples to the dataset, you can use the save_albumentations_augmentations operator, which will save the augmented samples to the dataset while keeping the augmented tag on the samples.

"},{"location":"integrations/fiftyone/#get-last-transformation-info","title":"Get last transformation info","text":"

When you apply a transformation pipeline to samples in your dataset using the FiftyOne Albumentations plugin, this information is captured and stored using FiftyOne's custom runs. This means that you can easily access the information about the last applied transformation.

In the FiftyOne App, you can use the get_last_albumentations_run_info operator to display a formatted summary of the relevant information:

Note

You can also access this information programmatically by getting info about the custom run that the information is stored in. For the Albumentations plugin, this info is stored via the key '_last_albumentations_run':

Python
last_run_info = dataset.get_run_info(\"_last_albumentations_run\")\nprint(last_run_info)\n
"},{"location":"integrations/fiftyone/#save-transformations","title":"Save transformations","text":"

If you are satisfied with the transformation pipeline you have created, you can save the entire composition of transformations to the dataset, hyperparameters and all. This means that after your rapid prototyping phase, you can easily move to a more reproducible workflow, and you can share your transformations or port them to other datasets.

To save a transformation pipeline, you can use the save_albumentations_transform operator:

After doing so, you will be able to view the information about this saved transformation pipeline using the get_albumentations_run_info operator:

Additionally, you will have access to this saved transformation pipeline under the \"saved\" tab for each transformation in the augment_with_albumentations operator modal.

"},{"location":"integrations/huggingface/","title":"HuggingFace","text":"
  • Image classification
  • Object Detection
"},{"location":"integrations/huggingface/image_classification_albumentations/","title":"Fine-tuning for Image Classification with \ud83e\udd17 Transformers","text":"

This notebook shows how to fine-tune any pretrained Vision model for Image Classification on a custom dataset. The idea is to add a randomly initialized classification head on top of a pre-trained encoder, and fine-tune the model altogether on a labeled dataset.

"},{"location":"integrations/huggingface/image_classification_albumentations/#imagefolder-feature","title":"ImageFolder feature","text":"

This notebook leverages the ImageFolder feature to easily run the notebook on a custom dataset (namely, EuroSAT in this tutorial). You can either load a Dataset from local folders or from local/remote files, like zip or tar.

"},{"location":"integrations/huggingface/image_classification_albumentations/#any-model","title":"Any model","text":"

This notebook is built to run on any image classification dataset with any vision model checkpoint from the Model Hub as long as that model has a version with a Image Classification head, such as: * ViT * Swin Transformer * ConvNeXT

  • in short, any model supported by AutoModelForImageClassification.
"},{"location":"integrations/huggingface/image_classification_albumentations/#albumentations","title":"Albumentations","text":"

In this notebook, we are going to leverage the Albumentations library for data augmentation. Note that we have other versions of this notebook available as well with other libraries including:

  • Torchvision's Transforms
  • Kornia
  • imgaug.

Depending on the model and the GPU you are using, you might need to adjust the batch size to avoid out-of-memory errors. Set those two parameters, then the rest of the notebook should run smoothly.

In this notebook, we'll fine-tune from the https://huggingface.co/facebook/convnext-tiny-224 checkpoint, but note that there are many, many more available on the hub.

Python
model_checkpoint = \"facebook/convnext-tiny-224\" # pre-trained model from which to fine-tune\nbatch_size = 32 # batch size for training and evaluation\n

Before we start, let's install the datasets, transformers and albumentations libraries.

Python
!pip install -q datasets transformers\n
\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 325 kB 8.7 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 4.0 MB 67.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 77 kB 8.1 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 1.1 MB 48.8 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 136 kB 72.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 212 kB 72.9 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 127 kB 75.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 895 kB 67.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 6.5 MB 56.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 596 kB 76.4 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 144 kB 76.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 94 kB 3.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 271 kB 77.3 MB/s \n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\ndatascience 0.10.6 requires folium==0.2.1, but you have folium 0.8.3 which is incompatible.\u001b[0m\n\u001b[?25h\n
Python
!pip install -q albumentations\n
\u001b[?25l\n

\u001b[K |\u258c | 10 kB 26.1 MB/s eta 0:00:01 \u001b[K |\u2588 | 20 kB 27.6 MB/s eta 0:00:01 \u001b[K |\u2588\u258b | 30 kB 11.8 MB/s eta 0:00:01 \u001b[K |\u2588\u2588 | 40 kB 8.9 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u258b | 51 kB 6.7 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u258f | 61 kB 7.9 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u258b | 71 kB 8.0 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u258f | 81 kB 7.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u258a | 92 kB 8.2 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u258f | 102 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u258a | 112 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 122 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 133 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 143 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 153 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 163 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 174 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 184 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 194 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 204 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 215 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 225 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 235 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 245 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 256 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 266 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 276 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 286 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 296 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 307 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 317 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 327 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 337 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 348 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 358 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 368 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 378 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 389 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 399 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 409 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 419 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 430 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 440 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 450 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 460 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 471 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 481 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 491 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 501 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 512 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 522 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 532 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 542 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 552 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 563 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 573 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 583 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 593 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 604 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f| 614 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b| 624 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 631 kB 8.4 MB/s \u001b[?25h Building wheel for imgaug (setup.py) ... \u001b[?25l\u001b[?25hdone

If you're opening this notebook locally, make sure your environment has an install from the last version of those libraries.

To be able to share your model with the community and generate results like the one shown in the picture below via the inference API, there are a few more steps to follow.

First you have to store your authentication token from the Hugging Face website (sign up here if you haven't already!) then execute the following cell and input your token:

Python
from huggingface_hub import notebook_login\n\nnotebook_login()\n
Login successful\nYour token has been saved to /root/.huggingface/token\n\u001b[1m\u001b[31mAuthenticated through git-credential store but this isn't the helper defined on your machine.\nYou might have to re-authenticate when pushing to the Hugging Face Hub. Run the following command in your terminal in case you want to set this credential helper as the default\n\ngit config --global credential.helper store\u001b[0m\n

Then you need to install Git-LFS to upload your model checkpoints:

Python
%%capture\n!sudo apt -qq install git-lfs\n!git config --global credential.helper store\n

We also quickly upload some telemetry - this tells us which examples and software versions are getting used so we know where to prioritize our maintenance efforts. We don't collect (or care about) any personally identifiable information, but if you'd prefer not to be counted, feel free to skip this step or delete this cell entirely.

Python
from transformers.utils import send_example_telemetry\n\nsend_example_telemetry(\"image_classification_albumentations_notebook\", framework=\"pytorch\")\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#fine-tuning-a-model-on-an-image-classification-task","title":"Fine-tuning a model on an image classification task","text":"

In this notebook, we will see how to fine-tune one of the \ud83e\udd17 Transformers vision models on an Image Classification dataset.

Given an image, the goal is to predict an appropriate class for it, like \"tiger\". The screenshot below is taken from a ViT fine-tuned on ImageNet-1k - try out the inference widget!

"},{"location":"integrations/huggingface/image_classification_albumentations/#loading-the-dataset","title":"Loading the dataset","text":"

We will use the \ud83e\udd17 Datasets library's ImageFolder feature to download our custom dataset into a DatasetDict.

In this case, the EuroSAT dataset is hosted remotely, so we provide the data_files argument. Alternatively, if you have local folders with images, you can load them using the data_dir argument.

Python
from datasets import load_dataset \n\n# load a custom dataset from local/remote files using the ImageFolder feature\n\n# option 1: local/remote files (supporting the following formats: tar, gzip, zip, xz, rar, zstd)\ndataset = load_dataset(\"imagefolder\", data_files=\"https://madm.dfki.de/files/sentinel/EuroSAT.zip\")\n\n# note that you can also provide several splits:\n# dataset = load_dataset(\"imagefolder\", data_files={\"train\": [\"path/to/file1\", \"path/to/file2\"], \"test\": [\"path/to/file3\", \"path/to/file4\"]})\n\n# note that you can push your dataset to the hub very easily (and reload afterwards using load_dataset)!\n# dataset.push_to_hub(\"nielsr/eurosat\")\n# dataset.push_to_hub(\"nielsr/eurosat\", private=True)\n\n# option 2: local folder\n# dataset = load_dataset(\"imagefolder\", data_dir=\"path_to_folder\")\n\n# option 3: just load any existing dataset from the hub ...\n# dataset = load_dataset(\"cifar10\")\n
Using custom data configuration default-0537267e6f812d56\n\n\nDownloading and preparing dataset image_folder/default to /root/.cache/huggingface/datasets/image_folder/default-0537267e6f812d56/0.0.0/ee92df8e96c6907f3c851a987be3fd03d4b93b247e727b69a8e23ac94392a091...\n\n\n\nDownloading data files: 0it [00:00, ?it/s]\n\n\n\nDownloading data files:   0%|          | 0/1 [00:00<?, ?it/s]\n\n\n\nDownloading data:   0%|          | 0.00/94.3M [00:00<?, ?B/s]\n\n\n\nExtracting data files:   0%|          | 0/1 [00:00<?, ?it/s]\n\n\n\nGenerating train split: 0 examples [00:00, ? examples/s]\n\n\nDataset image_folder downloaded and prepared to /root/.cache/huggingface/datasets/image_folder/default-0537267e6f812d56/0.0.0/ee92df8e96c6907f3c851a987be3fd03d4b93b247e727b69a8e23ac94392a091. Subsequent calls will reuse this data.\n\n\n\n  0%|          | 0/1 [00:00<?, ?it/s]\n

Let us also load the Accuracy metric, which we'll use to evaluate our model both during and after training.

Python
from datasets import load_metric\n\nmetric = load_metric(\"accuracy\")\n
Downloading builder script:   0%|          | 0.00/1.41k [00:00<?, ?B/s]\n

The dataset object itself is a DatasetDict, which contains one key per split (in this case, only \"train\" for a training split).

Python
dataset\n
DatasetDict({\n    train: Dataset({\n        features: ['image', 'label'],\n        num_rows: 27000\n    })\n})\n

To access an actual element, you need to select a split first, then give an index:

Python
example = dataset[\"train\"][10]\nexample\n
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=64x64 at 0x7FD62DA6B2D0>,\n 'label': 2}\n

Each example consists of an image and a corresponding label. We can also verify this by checking the features of the dataset:

Python
dataset[\"train\"].features\n
{'image': Image(decode=True, id=None),\n 'label': ClassLabel(num_classes=10, names=['AnnualCrop', 'Forest', 'HerbaceousVegetation', 'Highway', 'Industrial', 'Pasture', 'PermanentCrop', 'Residential', 'River', 'SeaLake'], id=None)}\n

The cool thing is that we can directly view the image (as the 'image' field is an Image feature), as follows:

Python
example['image']\n

Let's make it a little bigger as the images in the EuroSAT dataset are of low resolution (64x64 pixels):

Python
example['image'].resize((200, 200))\n

Let's check the corresponding label:

Python
example['label']\n
2\n

As you can see, the label field is not an actual string label. By default the ClassLabel fields are encoded into integers for convenience:

Python
dataset[\"train\"].features[\"label\"]\n
ClassLabel(num_classes=10, names=['AnnualCrop', 'Forest', 'HerbaceousVegetation', 'Highway', 'Industrial', 'Pasture', 'PermanentCrop', 'Residential', 'River', 'SeaLake'], id=None)\n

Let's create an id2label dictionary to decode them back to strings and see what they are. The inverse label2id will be useful too, when we load the model later.

Python
labels = dataset[\"train\"].features[\"label\"].names\nlabel2id, id2label = dict(), dict()\nfor i, label in enumerate(labels):\n    label2id[label] = i\n    id2label[i] = label\n\nid2label[2]\n
'HerbaceousVegetation'\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#preprocessing-the-data","title":"Preprocessing the data","text":"

Before we can feed these images to our model, we need to preprocess them.

Preprocessing images typically comes down to (1) resizing them to a particular size (2) normalizing the color channels (R,G,B) using a mean and standard deviation. These are referred to as image transformations.

In addition, one typically performs what is called data augmentation during training (like random cropping and flipping) to make the model more robust and achieve higher accuracy. Data augmentation is also a great technique to increase the size of the training data.

We will use Albumentations for the image transformations/data augmentation in this tutorial, but note that one can use any other package (like torchvision's transforms, imgaug, Kornia, etc.).

To make sure we (1) resize to the appropriate size (2) use the appropriate image mean and standard deviation for the model architecture we are going to use, we instantiate what is called an image processor with the AutoImageProcessor.from_pretrained method.

This image processor is a minimal preprocessor that can be used to prepare images for inference.

Python
from transformers import AutoImageProcessor\n\nimage_processor = AutoImageProcessor.from_pretrained(model_checkpoint)\nimage_processor\n
Could not find image processor class in the image processor config or the model config. Loading based on pattern matching with the model's feature extractor configuration.\n\n\n\n\n\nConvNextImageProcessor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_rescale\": true,\n  \"do_resize\": true,\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_processor_type\": \"ConvNextImageProcessor\",\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"rescale_factor\": 0.00392156862745098,\n  \"size\": {\n    \"shortest_edge\": 224\n  }\n}\n

The Datasets library is made for processing data very easily. We can write custom functions, which can then be applied on an entire dataset (either using .map() or .set_transform()).

Here we define 2 separate functions, one for training (which includes data augmentation) and one for validation (which only includes resizing, center cropping and normalizing).

Python
import cv2\nimport albumentations as A\nimport numpy as np\n\nif \"height\" in image_processor.size:\n    size = (image_processor.size[\"height\"], image_processor.size[\"width\"])\n    crop_size = size\n    max_size = None\nelif \"shortest_edge\" in image_processor.size:\n    size = image_processor.size[\"shortest_edge\"]\n    crop_size = (size, size)\n    max_size = image_processor.size.get(\"longest_edge\")\n\ntrain_transforms = A.Compose([\n    A.Resize(height=size, width=size),\n    A.RandomRotate90(),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n    A.Normalize(),\n])\n\nval_transforms = A.Compose([\n    A.Resize(height=size, width=size),\n    A.Normalize(),\n])\n\ndef preprocess_train(examples):\n    examples[\"pixel_values\"] = [\n        train_transforms(image=np.array(image))[\"image\"] for image in examples[\"image\"]\n    ]\n\n    return examples\n\ndef preprocess_val(examples):\n    examples[\"pixel_values\"] = [\n        val_transforms(image=np.array(image))[\"image\"] for image in examples[\"image\"]\n    ]\n\n    return examples\n

Next, we can preprocess our dataset by applying these functions. We will use the set_transform functionality, which allows to apply the functions above on-the-fly (meaning that they will only be applied when the images are loaded in RAM).

Python
# split up training into training + validation\nsplits = dataset[\"train\"].train_test_split(test_size=0.1)\ntrain_ds = splits['train']\nval_ds = splits['test']\n
Python
train_ds.set_transform(preprocess_train)\nval_ds.set_transform(preprocess_val)\n

Let's check the first example:

Python
train_ds[0]\n
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=64x64 at 0x7FD610178490>,\n 'label': 5,\n 'pixel_values': array([[[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052]],\n\n        [[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052]],\n\n        [[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.3986642 , -0.93277305, -0.4101089 ],\n         [-1.3986642 , -0.93277305, -0.4101089 ],\n         [-1.3986642 , -0.93277305, -0.4101089 ]],\n\n        ...,\n\n        [[-1.5014129 , -0.582633  , -0.35782132],\n         [-1.5014129 , -0.582633  , -0.35782132],\n         [-1.5014129 , -0.582633  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]],\n\n        [[-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]],\n\n        [[-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]]], dtype=float32)}\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#training-the-model","title":"Training the model","text":"

Now that our data is ready, we can download the pretrained model and fine-tune it. For classification we use the AutoModelForImageClassification class. Like with the image processor, the from_pretrained method will download and cache the model for us. As the label ids and the number of labels are dataset dependent, we pass num_labels, label2id, and id2label alongside the model_checkpoint he\u00a3re.

NOTE: in case you're planning to fine-tune an already fine-tuned checkpoint, like facebook/convnext-tiny-224 (which has already been fine-tuned on ImageNet-1k), then you need to provide the additional argument ignore_mismatched_sizes=True to the from_pretrained method. This will make sure the output head is thrown away and replaced by a new, randomly initialized classification head that includes a custom number of output neurons.

Python
from transformers import AutoModelForImageClassification, TrainingArguments, Trainer\n\nnum_labels = len(id2label)\nmodel = AutoModelForImageClassification.from_pretrained(\n    model_checkpoint, \n    label2id=label2id,\n    id2label=id2label,\n    ignore_mismatched_sizes = True, # provide this in case you'd like to fine-tune an already fine-tuned checkpoint\n)\n
Downloading:   0%|          | 0.00/68.0k [00:00<?, ?B/s]\n\n\n\nDownloading:   0%|          | 0.00/109M [00:00<?, ?B/s]\n\n\nSome weights of ConvNextForImageClassification were not initialized from the model checkpoint at facebook/convnext-tiny-224 and are newly initialized because the shapes did not match:\n- classifier.weight: found shape torch.Size([1000, 768]) in the checkpoint and torch.Size([10, 768]) in the model instantiated\n- classifier.bias: found shape torch.Size([1000]) in the checkpoint and torch.Size([10]) in the model instantiated\nYou should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n

The warning is telling us we are throwing away some weights (the weights and bias of the pooler layer) and randomly initializing some other (the weights and bias of the classifier layer). This is expected in this case, because we are adding a new head for which we don't have pretrained weights, so the library warns us we should fine-tune this model before using it for inference, which is exactly what we are going to do.

To instantiate a Trainer, we will need to define the training configuration and the evaluation metric. The most important is the TrainingArguments, which is a class that contains all the attributes to customize the training. It requires one folder name, which will be used to save the checkpoints of the model.

Most of the training arguments are pretty self-explanatory, but one that is quite important here is remove_unused_columns=False. This one will drop any features not used by the model's call function. By default it's True because usually it's ideal to drop unused feature columns, making it easier to unpack inputs into the model's call function. But, in our case, we need the unused features ('img' in particular) in order to create 'pixel_values'.

Python
model_name = model_checkpoint.split(\"/\")[-1]\n\nargs = TrainingArguments(\n    f\"{model_name}-finetuned-eurosat-albumentations\",\n    remove_unused_columns=False,\n    evaluation_strategy = \"epoch\",\n    save_strategy = \"epoch\",\n    learning_rate=5e-5,\n    per_device_train_batch_size=batch_size,\n    gradient_accumulation_steps=4,\n    per_device_eval_batch_size=batch_size,\n    num_train_epochs=3,\n    warmup_ratio=0.1,\n    logging_steps=10,\n    load_best_model_at_end=True,\n    metric_for_best_model=\"accuracy\",\n    push_to_hub=True,\n)\n

Here we set the evaluation to be done at the end of each epoch, tweak the learning rate, use the batch_size defined at the top of the notebook and customize the number of epochs for training, as well as the weight decay. Since the best model might not be the one at the end of training, we ask the Trainer to load the best model it saved (according to metric_name) at the end of training.

The last argument push_to_hub allows the Trainer to push the model to the Hub regularly during training. Remove it if you didn't follow the installation steps at the top of the notebook. If you want to save your model locally with a name that is different from the name of the repository, or if you want to push your model under an organization and not your name space, use the hub_model_id argument to set the repo name (it needs to be the full name, including your namespace: for instance \"nielsr/vit-finetuned-cifar10\" or \"huggingface/nielsr/vit-finetuned-cifar10\").

Next, we need to define a function for how to compute the metrics from the predictions, which will just use the metric we loaded earlier. The only preprocessing we have to do is to take the argmax of our predicted logits:

Python
import numpy as np\n\n# the compute_metrics function takes a Named Tuple as input:\n# predictions, which are the logits of the model as Numpy arrays,\n# and label_ids, which are the ground-truth labels as Numpy arrays.\ndef compute_metrics(eval_pred):\n    \"\"\"Computes accuracy on a batch of predictions\"\"\"\n    predictions = np.argmax(eval_pred.predictions, axis=1)\n    return metric.compute(predictions=predictions, references=eval_pred.label_ids)\n

We also define a collate_fn, which will be used to batch examples together. Each batch consists of 2 keys, namely pixel_values and labels.

Python
import torch\n\ndef collate_fn(examples):\n    images = []\n    labels = []\n    for example in examples:\n        image = np.moveaxis(example[\"pixel_values\"], source=2, destination=0)\n        images.append(torch.from_numpy(image))\n        labels.append(example[\"label\"])\n\n    pixel_values = torch.stack(images)\n    labels = torch.tensor(labels)\n    return {\"pixel_values\": pixel_values, \"labels\": labels}\n

Then we just need to pass all of this along with our datasets to the Trainer:

Python
trainer = Trainer(\n    model,\n    args,\n    train_dataset=train_ds,\n    eval_dataset=val_ds,\n    tokenizer=image_processor,\n    compute_metrics=compute_metrics,\n    data_collator=collate_fn,\n)\n
/content/convnext-tiny-224-finetuned-eurosat-albumentations is already a clone of https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations. Make sure you pull the latest changes with `repo.git_pull()`.\n

You might wonder why we pass along the image_processor as a tokenizer when we already preprocessed our data. This is only to make sure the image processor configuration file (stored as JSON) will also be uploaded to the repo on the hub.

Now we can finetune our model by calling the train method:

Python
trainer.train()\n
/usr/local/lib/python3.7/dist-packages/transformers/optimization.py:309: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning\n  FutureWarning,\n***** Running training *****\n  Num examples = 24300\n  Num Epochs = 3\n  Instantaneous batch size per device = 32\n  Total train batch size (w. parallel, distributed & accumulation) = 128\n  Gradient Accumulation steps = 4\n  Total optimization steps = 570\n\n\n\n\n<div>\n\n  <progress value='570' max='570' style='width:300px; height:20px; vertical-align: middle;'></progress>\n  [570/570 15:59, Epoch 3/3]\n</div>\n<table border=\"1\" class=\"dataframe\">\n
Epoch Training Loss Validation Loss Accuracy 1 0.141000 0.149633 0.954444 2 0.073600 0.095782 0.971852 3 0.056800 0.072716 0.974815

***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n\n\nTraining completed. Do not forget to share your model on huggingface.co/models =)\n\n\nLoading best model from convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570 (score: 0.9748148148148148).\n\n\n\n\n\nTrainOutput(global_step=570, training_loss=0.34729809766275843, metrics={'train_runtime': 961.6293, 'train_samples_per_second': 75.809, 'train_steps_per_second': 0.593, 'total_flos': 1.8322098956292096e+18, 'train_loss': 0.34729809766275843, 'epoch': 3.0})\n

We can check with the evaluate method that our Trainer did reload the best model properly (if it was not the last one):

Python
metrics = trainer.evaluate()\nprint(metrics)\n
***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\n
[85/85 00:12]
{'eval_loss': 0.0727163776755333, 'eval_accuracy': 0.9748148148148148, 'eval_runtime': 13.0419, 'eval_samples_per_second': 207.026, 'eval_steps_per_second': 6.517, 'epoch': 3.0}\n

You can now upload the result of the training to the Hub, just execute this instruction (note that the Trainer will automatically create a model card for you, as well as adding Tensorboard metrics - see the \"Training metrics\" tab!):

Python
trainer.push_to_hub()\n
Saving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n\n\n\nUpload file runs/Apr12_12-03-24_1ad162e1ead9/events.out.tfevents.1649765159.1ad162e1ead9.73.4:  24%|##4       \u2026\n\n\n\nUpload file runs/Apr12_12-03-24_1ad162e1ead9/events.out.tfevents.1649767032.1ad162e1ead9.73.6: 100%|##########\u2026\n\n\nTo https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\n   c500b3f..2143b42  main -> main\n\nTo https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\n   2143b42..71339cf  main -> main\n\n\n\n\n\n\n'https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/commit/2143b423b5cacdde6daebd3ee2b5971ecab463f6'\n

You can now share this model with all your friends, family, favorite pets: they can all load it with the identifier \"your-username/the-name-you-picked\" so for instance:

Python
from transformers import AutoModelForImageClassification, AutoImageProcessor\n\nimage_processor = AutoImageProcessor.from_pretrained(\"nielsr/my-awesome-model\")\nmodel = AutoModelForImageClassification.from_pretrained(\"nielsr/my-awesome-model\")\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#inference","title":"Inference","text":"

Let's say you have a new image, on which you'd like to make a prediction. Let's load a satellite image of a highway (that's not part of the EuroSAT dataset), and see how the model does.

Python
from PIL import Image\nimport requests\n\nurl = 'https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/highway.jpg'\nimage = Image.open(requests.get(url, stream=True).raw)\nimage\n

We'll load the image processor and model from the hub (here, we use the Auto Classes, which will make sure the appropriate classes will be loaded automatically based on the config.json and preprocessor_config.json files of the repo on the hub):

Python
from transformers import AutoModelForImageClassification, AutoImageProcessor\n\nrepo_name = \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\"\n\nimage_processor = AutoImageProcessor.from_pretrained(repo_name)\nmodel = AutoModelForImageClassification.from_pretrained(repo_name)\n
https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmp04g0zg5n\n\n\n\nDownloading:   0%|          | 0.00/266 [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json in cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\ncreating metadata file for /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nloading feature extractor configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json from cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nFeature extractor ConvNextFeatureExtractor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_resize\": true,\n  \"feature_extractor_type\": \"ConvNextFeatureExtractor\",\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"size\": 224\n}\n\nhttps://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmpbf9y4q39\n\n\n\nDownloading:   0%|          | 0.00/1.03k [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json in cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\ncreating metadata file for /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nloading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nhttps://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmpzr_9yxjo\n\n\n\nDownloading:   0%|          | 0.00/106M [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin in cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\ncreating metadata file for /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nloading weights file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin from cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nAll model checkpoint weights were used when initializing ConvNextForImageClassification.\n\nAll the weights of ConvNextForImageClassification were initialized from the model checkpoint at nielsr/convnext-tiny-224-finetuned-eurosat-albumentations.\nIf your task is similar to the task the model of the checkpoint was trained on, you can already use ConvNextForImageClassification for predictions without further training.\n
Python
# prepare image for the model\nencoding = image_processor(image.convert(\"RGB\"), return_tensors=\"pt\")\nprint(encoding.pixel_values.shape)\n
torch.Size([1, 3, 224, 224])\n
Python
import torch\n\n# forward pass\nwith torch.no_grad():\n    outputs = model(**encoding)\n    logits = outputs.logits\n
Python
predicted_class_idx = logits.argmax(-1).item()\nprint(\"Predicted class:\", model.config.id2label[predicted_class_idx])\n
Predicted class: Highway\n

Looks like our model got it correct!

"},{"location":"integrations/huggingface/image_classification_albumentations/#pipeline-api","title":"Pipeline API","text":"

An alternative way to quickly perform inference with any model on the hub is by leveraging the Pipeline API, which abstracts away all the steps we did manually above for us. It will perform the preprocessing, forward pass and postprocessing all in a single object.

Let's showcase this for our trained model:

Python
from transformers import pipeline\n\npipe = pipeline(\"image-classification\", \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\")\n
loading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nloading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nloading weights file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin from cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nAll model checkpoint weights were used when initializing ConvNextForImageClassification.\n\nAll the weights of ConvNextForImageClassification were initialized from the model checkpoint at nielsr/convnext-tiny-224-finetuned-eurosat-albumentations.\nIf your task is similar to the task the model of the checkpoint was trained on, you can already use ConvNextForImageClassification for predictions without further training.\nloading feature extractor configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json from cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nFeature extractor ConvNextFeatureExtractor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_resize\": true,\n  \"feature_extractor_type\": \"ConvNextFeatureExtractor\",\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"size\": 224\n}\n
Python
pipe(image)\n
[{'label': 'Highway', 'score': 0.5163754224777222},\n {'label': 'River', 'score': 0.11824000626802444},\n {'label': 'AnnualCrop', 'score': 0.05467210337519646},\n {'label': 'PermanentCrop', 'score': 0.05066365748643875},\n {'label': 'Industrial', 'score': 0.049283623695373535}]\n

As we can see, it does not only show the class label with the highest probability, but does return the top 5 labels, with their corresponding scores. Note that the pipelines also work with local models and image_processor:

Python
pipe = pipeline(\"image-classification\", \n                model=model,\n                feature_extractor=image_processor)\n
Python
pipe(image)\n
[{'label': 'Highway', 'score': 0.5163754224777222},\n {'label': 'River', 'score': 0.11824000626802444},\n {'label': 'AnnualCrop', 'score': 0.05467210337519646},\n {'label': 'PermanentCrop', 'score': 0.05066365748643875},\n {'label': 'Industrial', 'score': 0.049283623695373535}]\n
Python
\n
"},{"location":"integrations/huggingface/object_detection/","title":"Object Detection","text":""},{"location":"integrations/huggingface/object_detection/#object-detection","title":"Object detection","text":"

Object detection is the computer vision task of detecting instances (such as humans, buildings, or cars) in an image. Object detection models receive an image as input and output coordinates of the bounding boxes and associated labels of the detected objects. An image can contain multiple objects, each with its own bounding box and a label (e.g. it can have a car and a building), and each object can be present in different parts of an image (e.g. the image can have several cars). This task is commonly used in autonomous driving for detecting things like pedestrians, road signs, and traffic lights. Other applications include counting objects in images, image search, and more.

In this guide, you will learn how to:

  1. Finetune DETR, a model that combines a convolutional backbone with an encoder-decoder Transformer, on the CPPE-5 dataset.
  2. Use your finetuned model for inference.

To see all architectures and checkpoints compatible with this task, we recommend checking the task-page

Before you begin, make sure you have all the necessary libraries installed:

Bash
pip install -q datasets transformers accelerate timm\npip install -q -U albumentations>=1.4.5 torchmetrics pycocotools\n

You'll use \ud83e\udd17 Datasets to load a dataset from the Hugging Face Hub, \ud83e\udd17 Transformers to train your model, and albumentations to augment the data.

We encourage you to share your model with the community. Log in to your Hugging Face account to upload it to the Hub. When prompted, enter your token to log in:

Python
>>> from huggingface_hub import notebook_login\n\n>>> notebook_login()\n

To get started, we'll define global constants, namely the model name and image size. For this tutorial, we'll use the conditional DETR model due to its faster convergence. Feel free to select any object detection model available in the transformers library.

Python
>>> MODEL_NAME = \"microsoft/conditional-detr-resnet-50\"  # or \"facebook/detr-resnet-50\"\n>>> IMAGE_SIZE = 480\n
"},{"location":"integrations/huggingface/object_detection/#load-the-cppe-5-dataset","title":"Load the CPPE-5 dataset","text":"

The CPPE-5 dataset contains images with annotations identifying medical personal protective equipment (PPE) in the context of the COVID-19 pandemic.

Start by loading the dataset and creating a validation split from train:

Python
>>> from datasets import load_dataset\n\n>>> cppe5 = load_dataset(\"cppe-5\")\n\n>>> if \"validation\" not in cppe5:\n...     split = cppe5[\"train\"].train_test_split(0.15, seed=1337)\n...     cppe5[\"train\"] = split[\"train\"]\n...     cppe5[\"validation\"] = split[\"test\"]\n\n>>> cppe5\nDatasetDict({\n    train: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 850\n    })\n    test: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 29\n    })\n    validation: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 150\n    })\n})\n

You'll see that this dataset has 1000 images for train and validation sets and a test set with 29 images.

To get familiar with the data, explore what the examples look like.

Python
>>> cppe5[\"train\"][0]\n{\n  'image_id': 366,\n  'image': <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=500x290>,\n  'width': 500,\n  'height': 500,\n  'objects': {\n    'id': [1932, 1933, 1934],\n    'area': [27063, 34200, 32431],\n    'bbox': [[29.0, 11.0, 97.0, 279.0],\n      [201.0, 1.0, 120.0, 285.0],\n      [382.0, 0.0, 113.0, 287.0]],\n    'category': [0, 0, 0]\n  }\n}\n

The examples in the dataset have the following fields: - image_id: the example image id - image: a PIL.Image.Image object containing the image - width: width of the image - height: height of the image - objects: a dictionary containing bounding box metadata for the objects in the image: - id: the annotation id - area: the area of the bounding box - bbox: the object's bounding box (in the COCO format ) - category: the object's category, with possible values including Coverall (0), Face_Shield (1), Gloves (2), Goggles (3) and Mask (4)

You may notice that the bbox field follows the COCO format, which is the format that the DETR model expects. However, the grouping of the fields inside objects differs from the annotation format DETR requires. You will need to apply some preprocessing transformations before using this data for training.

To get an even better understanding of the data, visualize an example in the dataset.

Python
>>> import numpy as np\n>>> import os\n>>> from PIL import Image, ImageDraw\n\n>>> image = cppe5[\"train\"][2][\"image\"]\n>>> annotations = cppe5[\"train\"][2][\"objects\"]\n>>> draw = ImageDraw.Draw(image)\n\n>>> categories = cppe5[\"train\"].features[\"objects\"].feature[\"category\"].names\n\n>>> id2label = {index: x for index, x in enumerate(categories, start=0)}\n>>> label2id = {v: k for k, v in id2label.items()}\n\n>>> for i in range(len(annotations[\"id\"])):\n...     box = annotations[\"bbox\"][i]\n...     class_idx = annotations[\"category\"][i]\n...     x, y, w, h = tuple(box)\n...     # Check if coordinates are normalized or not\n...     if max(box) > 1.0:\n...         # Coordinates are un-normalized, no need to re-scale them\n...         x1, y1 = int(x), int(y)\n...         x2, y2 = int(x + w), int(y + h)\n...     else:\n...         # Coordinates are normalized, re-scale them\n...         x1 = int(x * width)\n...         y1 = int(y * height)\n...         x2 = int((x + w) * width)\n...         y2 = int((y + h) * height)\n...     draw.rectangle((x, y, x + w, y + h), outline=\"red\", width=1)\n...     draw.text((x, y), id2label[class_idx], fill=\"white\")\n\n>>> image\n

To visualize the bounding boxes with associated labels, you can get the labels from the dataset's metadata, specifically the category field. You'll also want to create dictionaries that map a label id to a label class (id2label) and the other way around (label2id). You can use them later when setting up the model. Including these maps will make your model reusable by others if you share it on the Hugging Face Hub. Please note that, the part of above code that draws the bounding boxes assume that it is in COCO format (x_min, y_min, width, height). It has to be adjusted to work for other formats like (x_min, y_min, x_max, y_max).

As a final step of getting familiar with the data, explore it for potential issues. One common problem with datasets for object detection is bounding boxes that \"stretch\" beyond the edge of the image. Such \"runaway\" bounding boxes can raise errors during training and should be addressed. There are a few examples with this issue in this dataset. To keep things simple in this guide, we will set clip=True for BboxParams in transformations below.

"},{"location":"integrations/huggingface/object_detection/#preprocess-the-data","title":"Preprocess the data","text":"

To finetune a model, you must preprocess the data you plan to use to match precisely the approach used for the pre-trained model. [AutoImageProcessor] takes care of processing image data to create pixel_values, pixel_mask, and labels that a DETR model can train with. The image processor has some attributes that you won't have to worry about:

  • image_mean = [0.485, 0.456, 0.406 ]
  • image_std = [0.229, 0.224, 0.225]

These are the mean and standard deviation used to normalize images during the model pre-training. These values are crucial to replicate when doing inference or finetuning a pre-trained image model.

Instantiate the image processor from the same checkpoint as the model you want to finetune.

Python
>>> from transformers import AutoImageProcessor\n\n>>> MAX_SIZE = IMAGE_SIZE\n\n>>> image_processor = AutoImageProcessor.from_pretrained(\n...     MODEL_NAME,\n...     do_resize=True,\n...     size={\"max_height\": MAX_SIZE, \"max_width\": MAX_SIZE},\n...     do_pad=True,\n...     pad_size={\"height\": MAX_SIZE, \"width\": MAX_SIZE},\n... )\n

Before passing the images to the image_processor, apply two preprocessing transformations to the dataset: - Augmenting images - Reformatting annotations to meet DETR expectations

First, to make sure the model does not overfit on the training data, you can apply image augmentation with any data augmentation library. Here we use Albumentations. This library ensures that transformations affect the image and update the bounding boxes accordingly. The \ud83e\udd17 Datasets library documentation has a detailed guide on how to augment images for object detection, and it uses the exact same dataset as an example. Apply some geometric and color transformations to the image. For additional augmentation options, explore the Albumentations Demo Space.

Python
>>> import albumentations as A\n\n>>> train_augment_and_transform = A.Compose(\n...     [\n...         A.Perspective(p=0.1),\n...         A.HorizontalFlip(p=0.5),\n...         A.RandomBrightnessContrast(p=0.5),\n...         A.HueSaturationValue(p=0.1),\n...     ],\n...     bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"category\"], clip=True, min_area=25),\n... )\n\n>>> validation_transform = A.Compose(\n...     [A.NoOp()],\n...     bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"category\"], clip=True),\n... )\n

The image_processor expects the annotations to be in the following format: {'image_id': int, 'annotations': List[Dict]}, where each dictionary is a COCO object annotation. Let's add a function to reformat annotations for a single example:

Python
>>> def format_image_annotations_as_coco(image_id, categories, areas, bboxes):\n...     \"\"\"Format one set of image annotations to the COCO format\n\n...     Args:\n...         image_id (str): image id. e.g. \"0001\"\n...         categories (List[int]): list of categories/class labels corresponding to provided bounding boxes\n...         areas (List[float]): list of corresponding areas to provided bounding boxes\n...         bboxes (List[Tuple[float]]): list of bounding boxes provided in COCO format\n...             ([center_x, center_y, width, height] in absolute coordinates)\n\n...     Returns:\n...         dict: {\n...             \"image_id\": image id,\n...             \"annotations\": list of formatted annotations\n...         }\n...     \"\"\"\n...     annotations = []\n...     for category, area, bbox in zip(categories, areas, bboxes):\n...         formatted_annotation = {\n...             \"image_id\": image_id,\n...             \"category_id\": category,\n...             \"iscrowd\": 0,\n...             \"area\": area,\n...             \"bbox\": list(bbox),\n...         }\n...         annotations.append(formatted_annotation)\n\n...     return {\n...         \"image_id\": image_id,\n...         \"annotations\": annotations,\n...     }\n

Now you can combine the image and annotation transformations to use on a batch of examples:

Python
>>> def augment_and_transform_batch(examples, transform, image_processor, return_pixel_mask=False):\n...     \"\"\"Apply augmentations and format annotations in COCO format for object detection task\"\"\"\n\n...     images = []\n...     annotations = []\n...     for image_id, image, objects in zip(examples[\"image_id\"], examples[\"image\"], examples[\"objects\"]):\n...         image = np.array(image.convert(\"RGB\"))\n\n...         # apply augmentations\n...         output = transform(image=image, bboxes=objects[\"bbox\"], category=objects[\"category\"])\n...         images.append(output[\"image\"])\n\n...         # format annotations in COCO format\n...         formatted_annotations = format_image_annotations_as_coco(\n...             image_id, output[\"category\"], objects[\"area\"], output[\"bboxes\"]\n...         )\n...         annotations.append(formatted_annotations)\n\n...     # Apply the image processor transformations: resizing, rescaling, normalization\n...     result = image_processor(images=images, annotations=annotations, return_tensors=\"pt\")\n\n...     if not return_pixel_mask:\n...         result.pop(\"pixel_mask\", None)\n\n...     return result\n

Apply this preprocessing function to the entire dataset using \ud83e\udd17 Datasets [~datasets.Dataset.with_transform] method. This method applies transformations on the fly when you load an element of the dataset.

At this point, you can check what an example from the dataset looks like after the transformations. You should see a tensor with pixel_values, a tensor with pixel_mask, and labels.

Python
>>> from functools import partial\n\n>>> # Make transform functions for batch and apply for dataset splits\n>>> train_transform_batch = partial(\n...     augment_and_transform_batch, transform=train_augment_and_transform, image_processor=image_processor\n... )\n>>> validation_transform_batch = partial(\n...     augment_and_transform_batch, transform=validation_transform, image_processor=image_processor\n... )\n\n>>> cppe5[\"train\"] = cppe5[\"train\"].with_transform(train_transform_batch)\n>>> cppe5[\"validation\"] = cppe5[\"validation\"].with_transform(validation_transform_batch)\n>>> cppe5[\"test\"] = cppe5[\"test\"].with_transform(validation_transform_batch)\n\n>>> cppe5[\"train\"][15]\n{'pixel_values': tensor([[[ 1.9235,  1.9407,  1.9749,  ..., -0.7822, -0.7479, -0.6965],\n          [ 1.9578,  1.9749,  1.9920,  ..., -0.7993, -0.7650, -0.7308],\n          [ 2.0092,  2.0092,  2.0263,  ..., -0.8507, -0.8164, -0.7822],\n          ...,\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741],\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741],\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741]],\n\n          [[ 1.6232,  1.6408,  1.6583,  ...,  0.8704,  1.0105,  1.1331],\n          [ 1.6408,  1.6583,  1.6758,  ...,  0.8529,  0.9930,  1.0980],\n          [ 1.6933,  1.6933,  1.7108,  ...,  0.8179,  0.9580,  1.0630],\n          ...,\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052],\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052],\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052]],\n\n          [[ 1.8905,  1.9080,  1.9428,  ..., -0.1487, -0.0964, -0.0615],\n          [ 1.9254,  1.9428,  1.9603,  ..., -0.1661, -0.1138, -0.0790],\n          [ 1.9777,  1.9777,  1.9951,  ..., -0.2010, -0.1138, -0.0790],\n          ...,\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265],\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265],\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265]]]),\n  'labels': {'image_id': tensor([688]), 'class_labels': tensor([3, 4, 2, 0, 0]), 'boxes': tensor([[0.4700, 0.1933, 0.1467, 0.0767],\n          [0.4858, 0.2600, 0.1150, 0.1000],\n          [0.4042, 0.4517, 0.1217, 0.1300],\n          [0.4242, 0.3217, 0.3617, 0.5567],\n          [0.6617, 0.4033, 0.5400, 0.4533]]), 'area': tensor([ 4048.,  4140.,  5694., 72478., 88128.]), 'iscrowd': tensor([0, 0, 0, 0, 0]), 'orig_size': tensor([480, 480])}}\n

You have successfully augmented the individual images and prepared their annotations. However, preprocessing isn't complete yet. In the final step, create a custom collate_fn to batch images together. Pad images (which are now pixel_values) to the largest image in a batch, and create a corresponding pixel_mask to indicate which pixels are real (1) and which are padding (0).

Python
>>> import torch\n\n>>> def collate_fn(batch):\n...     data = {}\n...     data[\"pixel_values\"] = torch.stack([x[\"pixel_values\"] for x in batch])\n...     data[\"labels\"] = [x[\"labels\"] for x in batch]\n...     if \"pixel_mask\" in batch[0]:\n...         data[\"pixel_mask\"] = torch.stack([x[\"pixel_mask\"] for x in batch])\n...     return data\n
"},{"location":"integrations/huggingface/object_detection/#preparing-function-to-compute-map","title":"Preparing function to compute mAP","text":"

Object detection models are commonly evaluated with a set of COCO-style metrics. We are going to use torchmetrics to compute mAP (mean average precision) and mAR (mean average recall) metrics and will wrap it to compute_metrics function in order to use in [Trainer] for evaluation.

Intermediate format of boxes used for training is YOLO (normalized) but we will compute metrics for boxes in Pascal VOC (absolute) format in order to correctly handle box areas. Let's define a function that converts bounding boxes to Pascal VOC format:

Python
>>> from transformers.image_transforms import center_to_corners_format\n\n>>> def convert_bbox_yolo_to_pascal(boxes, image_size):\n...     \"\"\"\n...     Convert bounding boxes from YOLO format (x_center, y_center, width, height) in range [0, 1]\n...     to Pascal VOC format (x_min, y_min, x_max, y_max) in absolute coordinates.\n\n...     Args:\n...         boxes (torch.Tensor): Bounding boxes in YOLO format\n...         image_size (Tuple[int, int]): Image size in format (height, width)\n\n...     Returns:\n...         torch.Tensor: Bounding boxes in Pascal VOC format (x_min, y_min, x_max, y_max)\n...     \"\"\"\n...     # convert center to corners format\n...     boxes = center_to_corners_format(boxes)\n\n...     # convert to absolute coordinates\n...     height, width = image_size\n...     boxes = boxes * torch.tensor([[width, height, width, height]])\n\n...     return boxes\n

Then, in compute_metrics function we collect predicted and target bounding boxes, scores and labels from evaluation loop results and pass it to the scoring function.

Python
>>> import numpy as np\n>>> from dataclasses import dataclass\n>>> from torchmetrics.detection.mean_ap import MeanAveragePrecision\n\n\n>>> @dataclass\n>>> class ModelOutput:\n...     logits: torch.Tensor\n...     pred_boxes: torch.Tensor\n\n\n>>> @torch.no_grad()\n>>> def compute_metrics(evaluation_results, image_processor, threshold=0.0, id2label=None):\n...     \"\"\"\n...     Compute mean average mAP, mAR and their variants for the object detection task.\n\n...     Args:\n...         evaluation_results (EvalPrediction): Predictions and targets from evaluation.\n...         threshold (float, optional): Threshold to filter predicted boxes by confidence. Defaults to 0.0.\n...         id2label (Optional[dict], optional): Mapping from class id to class name. Defaults to None.\n\n...     Returns:\n...         Mapping[str, float]: Metrics in a form of dictionary {<metric_name>: <metric_value>}\n...     \"\"\"\n\n...     predictions, targets = evaluation_results.predictions, evaluation_results.label_ids\n\n...     # For metric computation we need to provide:\n...     #  - targets in a form of list of dictionaries with keys \"boxes\", \"labels\"\n...     #  - predictions in a form of list of dictionaries with keys \"boxes\", \"scores\", \"labels\"\n\n...     image_sizes = []\n...     post_processed_targets = []\n...     post_processed_predictions = []\n\n...     # Collect targets in the required format for metric computation\n...     for batch in targets:\n...         # collect image sizes, we will need them for predictions post processing\n...         batch_image_sizes = torch.tensor(np.array([x[\"orig_size\"] for x in batch]))\n...         image_sizes.append(batch_image_sizes)\n...         # collect targets in the required format for metric computation\n...         # boxes were converted to YOLO format needed for model training\n...         # here we will convert them to Pascal VOC format (x_min, y_min, x_max, y_max)\n...         for image_target in batch:\n...             boxes = torch.tensor(image_target[\"boxes\"])\n...             boxes = convert_bbox_yolo_to_pascal(boxes, image_target[\"orig_size\"])\n...             labels = torch.tensor(image_target[\"class_labels\"])\n...             post_processed_targets.append({\"boxes\": boxes, \"labels\": labels})\n\n...     # Collect predictions in the required format for metric computation,\n...     # model produce boxes in YOLO format, then image_processor convert them to Pascal VOC format\n...     for batch, target_sizes in zip(predictions, image_sizes):\n...         batch_logits, batch_boxes = batch[1], batch[2]\n...         output = ModelOutput(logits=torch.tensor(batch_logits), pred_boxes=torch.tensor(batch_boxes))\n...         post_processed_output = image_processor.post_process_object_detection(\n...             output, threshold=threshold, target_sizes=target_sizes\n...         )\n...         post_processed_predictions.extend(post_processed_output)\n\n...     # Compute metrics\n...     metric = MeanAveragePrecision(box_format=\"xyxy\", class_metrics=True)\n...     metric.update(post_processed_predictions, post_processed_targets)\n...     metrics = metric.compute()\n\n...     # Replace list of per class metrics with separate metric for each class\n...     classes = metrics.pop(\"classes\")\n...     map_per_class = metrics.pop(\"map_per_class\")\n...     mar_100_per_class = metrics.pop(\"mar_100_per_class\")\n...     for class_id, class_map, class_mar in zip(classes, map_per_class, mar_100_per_class):\n...         class_name = id2label[class_id.item()] if id2label is not None else class_id.item()\n...         metrics[f\"map_{class_name}\"] = class_map\n...         metrics[f\"mar_100_{class_name}\"] = class_mar\n\n...     metrics = {k: round(v.item(), 4) for k, v in metrics.items()}\n\n...     return metrics\n\n\n>>> eval_compute_metrics_fn = partial(\n...     compute_metrics, image_processor=image_processor, id2label=id2label, threshold=0.0\n... )\n
"},{"location":"integrations/huggingface/object_detection/#training-the-detection-model","title":"Training the detection model","text":"

You have done most of the heavy lifting in the previous sections, so now you are ready to train your model! The images in this dataset are still quite large, even after resizing. This means that finetuning this model will require at least one GPU.

Training involves the following steps: 1. Load the model with [AutoModelForObjectDetection] using the same checkpoint as in the preprocessing. 2. Define your training hyperparameters in [TrainingArguments]. 3. Pass the training arguments to [Trainer] along with the model, dataset, image processor, and data collator. 4. Call [~Trainer.train] to finetune your model.

When loading the model from the same checkpoint that you used for the preprocessing, remember to pass the label2id and id2label maps that you created earlier from the dataset's metadata. Additionally, we specify ignore_mismatched_sizes=True to replace the existing classification head with a new one.

Python
>>> from transformers import AutoModelForObjectDetection\n\n>>> model = AutoModelForObjectDetection.from_pretrained(\n...     MODEL_NAME,\n...     id2label=id2label,\n...     label2id=label2id,\n...     ignore_mismatched_sizes=True,\n... )\n

In the [TrainingArguments] use output_dir to specify where to save your model, then configure hyperparameters as you see fit. For num_train_epochs=30 training will take about 35 minutes in Google Colab T4 GPU, increase the number of epoch to get better results.

Important notes: - Do not remove unused columns because this will drop the image column. Without the image column, you can't create pixel_values. For this reason, set remove_unused_columns to False. - Set eval_do_concat_batches=False to get proper evaluation results. Images have different number of target boxes, if batches are concatenated we will not be able to determine which boxes belongs to particular image.

If you wish to share your model by pushing to the Hub, set push_to_hub to True (you must be signed in to Hugging Face to upload your model).

Python
>>> from transformers import TrainingArguments\n\n>>> training_args = TrainingArguments(\n...     output_dir=\"detr_finetuned_cppe5\",\n...     num_train_epochs=30,\n...     fp16=False,\n...     per_device_train_batch_size=8,\n...     dataloader_num_workers=4,\n...     learning_rate=5e-5,\n...     lr_scheduler_type=\"cosine\",\n...     weight_decay=1e-4,\n...     max_grad_norm=0.01,\n...     metric_for_best_model=\"eval_map\",\n...     greater_is_better=True,\n...     load_best_model_at_end=True,\n...     eval_strategy=\"epoch\",\n...     save_strategy=\"epoch\",\n...     save_total_limit=2,\n...     remove_unused_columns=False,\n...     eval_do_concat_batches=False,\n...     push_to_hub=True,\n... )\n

Finally, bring everything together, and call [~transformers.Trainer.train]:

Python
>>> from transformers import Trainer\n\n>>> trainer = Trainer(\n...     model=model,\n...     args=training_args,\n...     train_dataset=cppe5[\"train\"],\n...     eval_dataset=cppe5[\"validation\"],\n...     processing_class=image_processor,\n...     data_collator=collate_fn,\n...     compute_metrics=eval_compute_metrics_fn,\n... )\n\n>>> trainer.train()\n
[3210/3210 26:07, Epoch 30/30] Epoch Training Loss Validation Loss Map Map 50 Map 75 Map Small Map Medium Map Large Mar 1 Mar 10 Mar 100 Mar Small Mar Medium Mar Large Map Coverall Mar 100 Coverall Map Face Shield Mar 100 Face Shield Map Gloves Mar 100 Gloves Map Goggles Mar 100 Goggles Map Mask Mar 100 Mask 1 No log 2.629903 0.008900 0.023200 0.006500 0.001300 0.002800 0.020500 0.021500 0.070400 0.101400 0.007600 0.106200 0.096100 0.036700 0.232000 0.000300 0.019000 0.003900 0.125400 0.000100 0.003100 0.003500 0.127600 2 No log 3.479864 0.014800 0.034600 0.010800 0.008600 0.011700 0.012500 0.041100 0.098700 0.130000 0.056000 0.062200 0.111900 0.053500 0.447300 0.010600 0.100000 0.000200 0.022800 0.000100 0.015400 0.009700 0.064400 3 No log 2.107622 0.041700 0.094000 0.034300 0.024100 0.026400 0.047400 0.091500 0.182800 0.225800 0.087200 0.199400 0.210600 0.150900 0.571200 0.017300 0.101300 0.007300 0.180400 0.002100 0.026200 0.031000 0.250200 4 No log 2.031242 0.055900 0.120600 0.046900 0.013800 0.038100 0.090300 0.105900 0.225600 0.266100 0.130200 0.228100 0.330000 0.191000 0.572100 0.010600 0.157000 0.014600 0.235300 0.001700 0.052300 0.061800 0.313800 5 3.889400 1.883433 0.089700 0.201800 0.067300 0.022800 0.065300 0.129500 0.136000 0.272200 0.303700 0.112900 0.312500 0.424600 0.300200 0.585100 0.032700 0.202500 0.031300 0.271000 0.008700 0.126200 0.075500 0.333800 6 3.889400 1.807503 0.118500 0.270900 0.090200 0.034900 0.076700 0.152500 0.146100 0.297800 0.325400 0.171700 0.283700 0.545900 0.396900 0.554500 0.043000 0.262000 0.054500 0.271900 0.020300 0.230800 0.077600 0.308000 7 3.889400 1.716169 0.143500 0.307700 0.123200 0.045800 0.097800 0.258300 0.165300 0.327700 0.352600 0.140900 0.336700 0.599400 0.442900 0.620700 0.069400 0.301300 0.081600 0.292000 0.011000 0.230800 0.112700 0.318200 8 3.889400 1.679014 0.153000 0.355800 0.127900 0.038700 0.115600 0.291600 0.176000 0.322500 0.349700 0.135600 0.326100 0.643700 0.431700 0.582900 0.069800 0.265800 0.088600 0.274600 0.028300 0.280000 0.146700 0.345300 9 3.889400 1.618239 0.172100 0.375300 0.137600 0.046100 0.141700 0.308500 0.194000 0.356200 0.386200 0.162400 0.359200 0.677700 0.469800 0.623900 0.102100 0.317700 0.099100 0.290200 0.029300 0.335400 0.160200 0.364000 10 1.599700 1.572512 0.179500 0.400400 0.147200 0.056500 0.141700 0.316700 0.213100 0.357600 0.381300 0.197900 0.344300 0.638500 0.466900 0.623900 0.101300 0.311400 0.104700 0.279500 0.051600 0.338500 0.173000 0.353300 11 1.599700 1.528889 0.192200 0.415000 0.160800 0.053700 0.150500 0.378000 0.211500 0.371700 0.397800 0.204900 0.374600 0.684800 0.491900 0.632400 0.131200 0.346800 0.122000 0.300900 0.038400 0.344600 0.177500 0.364400 12 1.599700 1.517532 0.198300 0.429800 0.159800 0.066400 0.162900 0.383300 0.220700 0.382100 0.405400 0.214800 0.383200 0.672900 0.469000 0.610400 0.167800 0.379700 0.119700 0.307100 0.038100 0.335400 0.196800 0.394200 13 1.599700 1.488849 0.209800 0.452300 0.172300 0.094900 0.171100 0.437800 0.222000 0.379800 0.411500 0.203800 0.397300 0.707500 0.470700 0.620700 0.186900 0.407600 0.124200 0.306700 0.059300 0.355400 0.207700 0.367100 14 1.599700 1.482210 0.228900 0.482600 0.187800 0.083600 0.191800 0.444100 0.225900 0.376900 0.407400 0.182500 0.384800 0.700600 0.512100 0.640100 0.175000 0.363300 0.144300 0.300000 0.083100 0.363100 0.229900 0.370700 15 1.326800 1.475198 0.216300 0.455600 0.174900 0.088500 0.183500 0.424400 0.226900 0.373400 0.404300 0.199200 0.396400 0.677800 0.496300 0.633800 0.166300 0.392400 0.128900 0.312900 0.085200 0.312300 0.205000 0.370200 16 1.326800 1.459697 0.233200 0.504200 0.192200 0.096000 0.202000 0.430800 0.239100 0.382400 0.412600 0.219500 0.403100 0.670400 0.485200 0.625200 0.196500 0.410100 0.135700 0.299600 0.123100 0.356900 0.225300 0.371100 17 1.326800 1.407340 0.243400 0.511900 0.204500 0.121000 0.215700 0.468000 0.246200 0.394600 0.424200 0.225900 0.416100 0.705200 0.494900 0.638300 0.224900 0.430400 0.157200 0.317900 0.115700 0.369200 0.224200 0.365300 18 1.326800 1.419522 0.245100 0.521500 0.210000 0.116100 0.211500 0.489900 0.255400 0.391600 0.419700 0.198800 0.421200 0.701400 0.501800 0.634200 0.226700 0.410100 0.154400 0.321400 0.105900 0.352300 0.236700 0.380400 19 1.158600 1.398764 0.253600 0.519200 0.213600 0.135200 0.207700 0.491900 0.257300 0.397300 0.428000 0.241400 0.401800 0.703500 0.509700 0.631100 0.236700 0.441800 0.155900 0.330800 0.128100 0.352300 0.237500 0.384000 20 1.158600 1.390591 0.248800 0.520200 0.216600 0.127500 0.211400 0.471900 0.258300 0.407000 0.429100 0.240300 0.407600 0.708500 0.505800 0.623400 0.235500 0.431600 0.150000 0.325000 0.125700 0.375400 0.227200 0.390200 21 1.158600 1.360608 0.262700 0.544800 0.222100 0.134700 0.230000 0.487500 0.269500 0.413300 0.436300 0.236200 0.419100 0.709300 0.514100 0.637400 0.257200 0.450600 0.165100 0.338400 0.139400 0.372300 0.237700 0.382700 22 1.158600 1.368296 0.262800 0.542400 0.236400 0.137400 0.228100 0.498500 0.266500 0.409000 0.433000 0.239900 0.418500 0.697500 0.520500 0.641000 0.257500 0.455700 0.162600 0.334800 0.140200 0.353800 0.233200 0.379600 23 1.158600 1.368176 0.264800 0.541100 0.233100 0.138200 0.223900 0.498700 0.272300 0.407400 0.434400 0.233100 0.418300 0.702000 0.524400 0.642300 0.262300 0.444300 0.159700 0.335300 0.140500 0.366200 0.236900 0.384000 24 1.049700 1.355271 0.269700 0.549200 0.239100 0.134700 0.229900 0.519200 0.274800 0.412700 0.437600 0.245400 0.417200 0.711200 0.523200 0.644100 0.272100 0.440500 0.166700 0.341500 0.137700 0.373800 0.249000 0.388000 25 1.049700 1.355180 0.272500 0.547900 0.243800 0.149700 0.229900 0.523100 0.272500 0.415700 0.442200 0.256200 0.420200 0.705800 0.523900 0.639600 0.271700 0.451900 0.166300 0.346900 0.153700 0.383100 0.247000 0.389300 26 1.049700 1.349337 0.275600 0.556300 0.246400 0.146700 0.234800 0.516300 0.274200 0.418300 0.440900 0.248700 0.418900 0.705800 0.523200 0.636500 0.274700 0.440500 0.172400 0.349100 0.155600 0.384600 0.252300 0.393800 27 1.049700 1.350782 0.275200 0.548700 0.246800 0.147300 0.236400 0.527200 0.280100 0.416200 0.442600 0.253400 0.424000 0.710300 0.526600 0.640100 0.273200 0.445600 0.167000 0.346900 0.160100 0.387700 0.249200 0.392900 28 1.049700 1.346533 0.277000 0.552800 0.252900 0.147400 0.240000 0.527600 0.280900 0.420900 0.444100 0.255500 0.424500 0.711200 0.530200 0.646800 0.277400 0.441800 0.170900 0.346900 0.156600 0.389200 0.249600 0.396000 29 0.993700 1.346575 0.277100 0.554800 0.252900 0.148400 0.239700 0.523600 0.278400 0.420000 0.443300 0.256300 0.424000 0.705600 0.529600 0.647300 0.273900 0.439200 0.174300 0.348700 0.157600 0.386200 0.250100 0.395100 30 0.993700 1.346446 0.277400 0.554700 0.252700 0.147900 0.240800 0.523600 0.278800 0.420400 0.443300 0.256100 0.424200 0.705500 0.530100 0.646800 0.275600 0.440500 0.174500 0.348700 0.157300 0.386200 0.249200 0.394200

If you have set `push_to_hub` to `True` in the `training_args`, the training checkpoints are pushed to the Hugging Face Hub. Upon training completion, push the final model to the Hub as well by calling the [`~transformers.Trainer.push_to_hub`] method. Python

>>> trainer.push_to_hub()\n
## Evaluate Python
>>> from pprint import pprint\n\n>>> metrics = trainer.evaluate(eval_dataset=cppe5[\"test\"], metric_key_prefix=\"test\")\n>>> pprint(metrics)\n{'epoch': 30.0,\n  'test_loss': 1.0877351760864258,\n  'test_map': 0.4116,\n  'test_map_50': 0.741,\n  'test_map_75': 0.3663,\n  'test_map_Coverall': 0.5937,\n  'test_map_Face_Shield': 0.5863,\n  'test_map_Gloves': 0.3416,\n  'test_map_Goggles': 0.1468,\n  'test_map_Mask': 0.3894,\n  'test_map_large': 0.5637,\n  'test_map_medium': 0.3257,\n  'test_map_small': 0.3589,\n  'test_mar_1': 0.323,\n  'test_mar_10': 0.5237,\n  'test_mar_100': 0.5587,\n  'test_mar_100_Coverall': 0.6756,\n  'test_mar_100_Face_Shield': 0.7294,\n  'test_mar_100_Gloves': 0.4721,\n  'test_mar_100_Goggles': 0.4125,\n  'test_mar_100_Mask': 0.5038,\n  'test_mar_large': 0.7283,\n  'test_mar_medium': 0.4901,\n  'test_mar_small': 0.4469,\n  'test_runtime': 1.6526,\n  'test_samples_per_second': 17.548,\n  'test_steps_per_second': 2.42}\n
These results can be further improved by adjusting the hyperparameters in [`TrainingArguments`]. Give it a go! ## Inference Now that you have finetuned a model, evaluated it, and uploaded it to the Hugging Face Hub, you can use it for inference. Python
>>> import torch\n>>> import requests\n\n>>> from PIL import Image, ImageDraw\n>>> from transformers import AutoImageProcessor, AutoModelForObjectDetection\n\n>>> url = \"https://images.pexels.com/photos/8413299/pexels-photo-8413299.jpeg?auto=compress&cs=tinysrgb&w=630&h=375&dpr=2\"\n>>> image = Image.open(requests.get(url, stream=True).raw)\n
Load model and image processor from the Hugging Face Hub (skip to use already trained in this session): Python
>>> from accelerate.test_utils.testing import get_backend\n# automatically detects the underlying device type (CUDA, CPU, XPU, MPS, etc.)\n>>> device, _, _ = get_backend()\n>>> model_repo = \"qubvel-hf/detr_finetuned_cppe5\"\n\n>>> image_processor = AutoImageProcessor.from_pretrained(model_repo)\n>>> model = AutoModelForObjectDetection.from_pretrained(model_repo)\n>>> model = model.to(device)\n
And detect bounding boxes: Python
>>> with torch.no_grad():\n...     inputs = image_processor(images=[image], return_tensors=\"pt\")\n...     outputs = model(**inputs.to(device))\n...     target_sizes = torch.tensor([[image.size[1], image.size[0]]])\n...     results = image_processor.post_process_object_detection(outputs, threshold=0.3, target_sizes=target_sizes)[0]\n\n>>> for score, label, box in zip(results[\"scores\"], results[\"labels\"], results[\"boxes\"]):\n...     box = [round(i, 2) for i in box.tolist()]\n...     print(\n...         f\"Detected {model.config.id2label[label.item()]} with confidence \"\n...         f\"{round(score.item(), 3)} at location {box}\"\n...     )\nDetected Gloves with confidence 0.683 at location [244.58, 124.33, 300.35, 185.13]\nDetected Mask with confidence 0.517 at location [143.73, 64.58, 219.57, 125.89]\nDetected Gloves with confidence 0.425 at location [179.15, 155.57, 262.4, 226.35]\nDetected Coverall with confidence 0.407 at location [307.13, -1.18, 477.82, 318.06]\nDetected Coverall with confidence 0.391 at location [68.61, 126.66, 309.03, 318.89]\n
Let's plot the result: Python
>>> draw = ImageDraw.Draw(image)\n\n>>> for score, label, box in zip(results[\"scores\"], results[\"labels\"], results[\"boxes\"]):\n...     box = [round(i, 2) for i in box.tolist()]\n...     x, y, x2, y2 = tuple(box)\n...     draw.rectangle((x, y, x2, y2), outline=\"red\", width=1)\n...     draw.text((x, y), model.config.id2label[label.item()], fill=\"white\")\n\n>>> image\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/","title":"How to Train RT-DETR on Custom Dataset with Roboflow, HuggingFace and Albumentations","text":""},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#how-to-train-rt-detr-on-custom-dataset","title":"How to Train RT-DETR on Custom Dataset","text":"

RT-DETR, short for \"Real-Time DEtection TRansformer\", is a computer vision model developed by Peking University and Baidu. In their paper, \"DETRs Beat YOLOs on Real-time Object Detection\" the authors claim that RT-DETR can outperform YOLO models in object detection, both in terms of speed and accuracy. The model has been released under the Apache 2.0 license, making it a great option, especially for enterprise projects.

Recently, RT-DETR was added to the transformers library, significantly simplifying its fine-tuning process. In this tutorial, we will show you how to train RT-DETR on a custom dataset.

"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#setup","title":"Setup","text":""},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#configure-your-api-keys","title":"Configure your API keys","text":"

To fine-tune RT-DETR, you need to provide your HuggingFace Token and Roboflow API key. Follow these steps:

  • Open your HuggingFace Settings page. Click Access Tokens then New Token to generate new token.
  • Go to your Roboflow Settings page. Click Copy. This will place your private key in the clipboard.
  • In Colab, go to the left pane and click on Secrets (\ud83d\udd11).
    • Store HuggingFace Access Token under the name HF_TOKEN.
    • Store Roboflow API Key under the name ROBOFLOW_API_KEY.
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#select-the-runtime","title":"Select the runtime","text":"

Let's make sure that we have access to GPU. We can use nvidia-smi command to do that. In case of any problems navigate to Edit -> Notebook settings -> Hardware accelerator, set it to L4 GPU, and then click Save.

Python
!nvidia-smi\n
Thu Jul 11 09:20:53 2024       \n+---------------------------------------------------------------------------------------+\n| NVIDIA-SMI 535.104.05             Driver Version: 535.104.05   CUDA Version: 12.2     |\n|-----------------------------------------+----------------------+----------------------+\n| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |\n|                                         |                      |               MIG M. |\n|=========================================+======================+======================|\n|   0  Tesla T4                       Off | 00000000:00:04.0 Off |                    0 |\n| N/A   65C    P8              11W /  70W |      0MiB / 15360MiB |      0%      Default |\n|                                         |                      |                  N/A |\n+-----------------------------------------+----------------------+----------------------+\n\n+---------------------------------------------------------------------------------------+\n| Processes:                                                                            |\n|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |\n|        ID   ID                                                             Usage      |\n|=======================================================================================|\n|  No running processes found                                                           |\n+---------------------------------------------------------------------------------------+\n

NOTE: To make it easier for us to manage datasets, images and models we create a HOME constant.

Python
import os\nHOME = os.getcwd()\nprint(\"HOME:\", HOME)\n
HOME: /content\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#install-dependencies","title":"Install dependencies","text":"Python
!pip install -q git+https://github.com/huggingface/transformers.git\n!pip install -q git+https://github.com/roboflow/supervision.git\n!pip install -q accelerate\n!pip install -q roboflow\n!pip install -q torchmetrics\n!pip install -q \"albumentations>=1.4.5\"\n
  Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n  Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n  Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Building wheel for transformers (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n  Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n  Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Building wheel for supervision (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m314.1/314.1 kB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m21.3/21.3 MB\u001b[0m \u001b[31m71.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m76.2/76.2 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m178.7/178.7 kB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m54.5/54.5 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m868.8/868.8 kB\u001b[0m \u001b[31m6.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m165.3/165.3 kB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m14.9/14.9 MB\u001b[0m \u001b[31m83.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m13.4/13.4 MB\u001b[0m \u001b[31m92.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m313.5/313.5 kB\u001b[0m \u001b[31m34.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25h\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#imports","title":"Imports","text":"Python
import torch\nimport requests\n\nimport numpy as np\nimport supervision as sv\nimport albumentations as A\n\nfrom PIL import Image\nfrom pprint import pprint\nfrom roboflow import Roboflow\nfrom dataclasses import dataclass, replace\nfrom google.colab import userdata\nfrom torch.utils.data import Dataset\nfrom transformers import (\n    AutoImageProcessor,\n    AutoModelForObjectDetection,\n    TrainingArguments,\n    Trainer\n)\nfrom torchmetrics.detection.mean_ap import MeanAveragePrecision\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#inference-with-pre-trained-rt-detr-model","title":"Inference with pre-trained RT-DETR model","text":"Python
# @title Load model\n\nCHECKPOINT = \"PekingU/rtdetr_r50vd_coco_o365\"\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = AutoModelForObjectDetection.from_pretrained(CHECKPOINT).to(DEVICE)\nprocessor = AutoImageProcessor.from_pretrained(CHECKPOINT)\n
config.json:   0%|          | 0.00/5.11k [00:00<?, ?B/s]\n\n\n\nmodel.safetensors:   0%|          | 0.00/172M [00:00<?, ?B/s]\n\n\n\npreprocessor_config.json:   0%|          | 0.00/841 [00:00<?, ?B/s]\n
Python
# @title Run inference\n\nURL = \"https://media.roboflow.com/notebooks/examples/dog.jpeg\"\n\nimage = Image.open(requests.get(URL, stream=True).raw)\ninputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\nwith torch.no_grad():\n    outputs = model(**inputs)\n\nw, h = image.size\nresults = processor.post_process_object_detection(\n    outputs, target_sizes=[(h, w)], threshold=0.3)\n
Python
# @title Display result with NMS\n\ndetections = sv.Detections.from_transformers(results[0])\nlabels = [\n    model.config.id2label[class_id]\n    for class_id\n    in detections.class_id\n]\n\nannotated_image = image.copy()\nannotated_image = sv.BoundingBoxAnnotator().annotate(annotated_image, detections)\nannotated_image = sv.LabelAnnotator().annotate(annotated_image, detections, labels=labels)\nannotated_image.thumbnail((600, 600))\nannotated_image\n
Python
# @title Display result with NMS\n\ndetections = sv.Detections.from_transformers(results[0]).with_nms(threshold=0.1)\nlabels = [\n    model.config.id2label[class_id]\n    for class_id\n    in detections.class_id\n]\n\nannotated_image = image.copy()\nannotated_image = sv.BoundingBoxAnnotator().annotate(annotated_image, detections)\nannotated_image = sv.LabelAnnotator().annotate(annotated_image, detections, labels=labels)\nannotated_image.thumbnail((600, 600))\nannotated_image\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#fine-tune-rt-detr-on-custom-dataset","title":"Fine-tune RT-DETR on custom dataset","text":"Python
# @title Download dataset from Roboflow Universe\n\nROBOFLOW_API_KEY = userdata.get('ROBOFLOW_API_KEY')\nrf = Roboflow(api_key=ROBOFLOW_API_KEY)\n\nproject = rf.workspace(\"roboflow-jvuqo\").project(\"poker-cards-fmjio\")\nversion = project.version(4)\ndataset = version.download(\"coco\")\n
loading Roboflow workspace...\nloading Roboflow project...\n\n\nDownloading Dataset Version Zip in poker-cards-4 to coco:: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 39123/39123 [00:01<00:00, 27288.54it/s]\n\n\n\n\n\nExtracting Dataset Version Zip to poker-cards-4 in coco:: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 907/907 [00:00<00:00, 2984.59it/s]\n
Python
ds_train = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/train\",\n    annotations_path=f\"{dataset.location}/train/_annotations.coco.json\",\n)\nds_valid = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/valid\",\n    annotations_path=f\"{dataset.location}/valid/_annotations.coco.json\",\n)\nds_test = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/test\",\n    annotations_path=f\"{dataset.location}/test/_annotations.coco.json\",\n)\n\nprint(f\"Number of training images: {len(ds_train)}\")\nprint(f\"Number of validation images: {len(ds_valid)}\")\nprint(f\"Number of test images: {len(ds_test)}\")\n
Number of training images: 811\nNumber of validation images: 44\nNumber of test images: 44\n
Python
# @title Display dataset sample\n\nGRID_SIZE = 5\n\ndef annotate(image, annotations, classes):\n    labels = [\n        classes[class_id]\n        for class_id\n        in annotations.class_id\n    ]\n\n    bounding_box_annotator = sv.BoundingBoxAnnotator()\n    label_annotator = sv.LabelAnnotator(text_scale=1, text_thickness=2)\n\n    annotated_image = image.copy()\n    annotated_image = bounding_box_annotator.annotate(annotated_image, annotations)\n    annotated_image = label_annotator.annotate(annotated_image, annotations, labels=labels)\n    return annotated_image\n\nannotated_images = []\nfor i in range(GRID_SIZE * GRID_SIZE):\n    _, image, annotations = ds_train[i]\n    annotated_image = annotate(image, annotations, ds_train.classes)\n    annotated_images.append(annotated_image)\n\ngrid = sv.create_tiles(\n    annotated_images,\n    grid_size=(GRID_SIZE, GRID_SIZE),\n    single_tile_size=(400, 400),\n    tile_padding_color=sv.Color.WHITE,\n    tile_margin_color=sv.Color.WHITE\n)\nsv.plot_image(grid, size=(10, 10))\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#preprocess-the-data","title":"Preprocess the data","text":"

To finetune a model, you must preprocess the data you plan to use to match precisely the approach used for the pre-trained model. AutoImageProcessor takes care of processing image data to create pixel_values, pixel_mask, and labels that a DETR model can train with. The image processor has some attributes that you won't have to worry about:

  • image_mean = [0.485, 0.456, 0.406 ]
  • image_std = [0.229, 0.224, 0.225]

These are the mean and standard deviation used to normalize images during the model pre-training. These values are crucial to replicate when doing inference or finetuning a pre-trained image model.

Instantiate the image processor from the same checkpoint as the model you want to finetune.

Python
IMAGE_SIZE = 480\n\nprocessor = AutoImageProcessor.from_pretrained(\n    CHECKPOINT,\n    do_resize=True,\n    size={\"width\": IMAGE_SIZE, \"height\": IMAGE_SIZE},\n)\n

Before passing the images to the processor, apply two preprocessing transformations to the dataset:

  • Augmenting images
  • Reformatting annotations to meet RT-DETR expectations

First, to make sure the model does not overfit on the training data, you can apply image augmentation with any data augmentation library. Here we use Albumentations. This library ensures that transformations affect the image and update the bounding boxes accordingly.

Python
train_augmentation_and_transform = A.Compose(\n    [\n        A.Perspective(p=0.1),\n        A.HorizontalFlip(p=0.5),\n        A.RandomBrightnessContrast(p=0.5),\n        A.HueSaturationValue(p=0.1),\n    ],\n    bbox_params=A.BboxParams(\n        format=\"pascal_voc\",\n        label_fields=[\"category\"],\n        clip=True,\n        min_area=25\n    ),\n)\n\nvalid_transform = A.Compose(\n    [A.NoOp()],\n    bbox_params=A.BboxParams(\n        format=\"pascal_voc\",\n        label_fields=[\"category\"],\n        clip=True,\n        min_area=1\n    ),\n)\n
Python
# @title Visualize some augmented images\n\nIMAGE_COUNT = 5\n\nfor i in range(IMAGE_COUNT):\n    _, image, annotations = ds_train[i]\n\n    output = train_augmentation_and_transform(\n        image=image,\n        bboxes=annotations.xyxy,\n        category=annotations.class_id\n    )\n\n    augmented_image = output[\"image\"]\n    augmented_annotations = replace(\n        annotations,\n        xyxy=np.array(output[\"bboxes\"]),\n        class_id=np.array(output[\"category\"])\n    )\n\n    annotated_images = [\n        annotate(image, annotations, ds_train.classes),\n        annotate(augmented_image, augmented_annotations, ds_train.classes)\n    ]\n    grid = sv.create_tiles(\n        annotated_images,\n        titles=['original', 'augmented'],\n        titles_scale=0.5,\n        single_tile_size=(400, 400),\n        tile_padding_color=sv.Color.WHITE,\n        tile_margin_color=sv.Color.WHITE\n    )\n    sv.plot_image(grid, size=(6, 6))\n

The processor expects the annotations to be in the following format: {'image_id': int, 'annotations': List[Dict]}, where each dictionary is a COCO object annotation. Let's add a function to reformat annotations for a single example:

Python
class PyTorchDetectionDataset(Dataset):\n    def __init__(self, dataset: sv.DetectionDataset, processor, transform: A.Compose = None):\n        self.dataset = dataset\n        self.processor = processor\n        self.transform = transform\n\n    @staticmethod\n    def annotations_as_coco(image_id, categories, boxes):\n        annotations = []\n        for category, bbox in zip(categories, boxes):\n            x1, y1, x2, y2 = bbox\n            formatted_annotation = {\n                \"image_id\": image_id,\n                \"category_id\": category,\n                \"bbox\": [x1, y1, x2 - x1, y2 - y1],\n                \"iscrowd\": 0,\n                \"area\": (x2 - x1) * (y2 - y1),\n            }\n            annotations.append(formatted_annotation)\n\n        return {\n            \"image_id\": image_id,\n            \"annotations\": annotations,\n        }\n\n    def __len__(self):\n        return len(self.dataset)\n\n    def __getitem__(self, idx):\n        _, image, annotations = self.dataset[idx]\n\n        # Convert image to RGB numpy array\n        image = image[:, :, ::-1]\n        boxes = annotations.xyxy\n        categories = annotations.class_id\n\n        if self.transform:\n            transformed = self.transform(\n                image=image,\n                bboxes=boxes,\n                category=categories\n            )\n            image = transformed[\"image\"]\n            boxes = transformed[\"bboxes\"]\n            categories = transformed[\"category\"]\n\n\n        formatted_annotations = self.annotations_as_coco(\n            image_id=idx, categories=categories, boxes=boxes)\n        result = self.processor(\n            images=image, annotations=formatted_annotations, return_tensors=\"pt\")\n\n        # Image processor expands batch dimension, lets squeeze it\n        result = {k: v[0] for k, v in result.items()}\n\n        return result\n

Now you can combine the image and annotation transformations to use on a batch of examples:

Python
pytorch_dataset_train = PyTorchDetectionDataset(\n    ds_train, processor, transform=train_augmentation_and_transform)\npytorch_dataset_valid = PyTorchDetectionDataset(\n    ds_valid, processor, transform=valid_transform)\npytorch_dataset_test = PyTorchDetectionDataset(\n    ds_test, processor, transform=valid_transform)\n\npytorch_dataset_train[15]\n
{'pixel_values': tensor([[[0.0745, 0.0745, 0.0745,  ..., 0.2431, 0.2471, 0.2471],\n          [0.0745, 0.0745, 0.0745,  ..., 0.2510, 0.2549, 0.2549],\n          [0.0667, 0.0706, 0.0706,  ..., 0.2588, 0.2588, 0.2588],\n          ...,\n          [0.0118, 0.0118, 0.0118,  ..., 0.0510, 0.0549, 0.0510],\n          [0.0157, 0.0196, 0.0235,  ..., 0.0549, 0.0627, 0.0549],\n          [0.0235, 0.0275, 0.0314,  ..., 0.0549, 0.0627, 0.0549]],\n\n         [[0.0549, 0.0549, 0.0549,  ..., 0.3137, 0.3176, 0.3176],\n          [0.0549, 0.0549, 0.0549,  ..., 0.3216, 0.3255, 0.3255],\n          [0.0471, 0.0510, 0.0510,  ..., 0.3294, 0.3294, 0.3294],\n          ...,\n          [0.0000, 0.0000, 0.0000,  ..., 0.0353, 0.0392, 0.0353],\n          [0.0000, 0.0000, 0.0039,  ..., 0.0392, 0.0471, 0.0392],\n          [0.0000, 0.0039, 0.0078,  ..., 0.0392, 0.0471, 0.0392]],\n\n         [[0.0431, 0.0431, 0.0431,  ..., 0.3922, 0.3961, 0.3961],\n          [0.0431, 0.0431, 0.0431,  ..., 0.4000, 0.4039, 0.4039],\n          [0.0353, 0.0392, 0.0392,  ..., 0.4078, 0.4078, 0.4078],\n          ...,\n          [0.0000, 0.0000, 0.0000,  ..., 0.0314, 0.0353, 0.0314],\n          [0.0000, 0.0000, 0.0039,  ..., 0.0353, 0.0431, 0.0353],\n          [0.0000, 0.0039, 0.0078,  ..., 0.0353, 0.0431, 0.0353]]]),\n 'labels': {'size': tensor([480, 480]), 'image_id': tensor([15]), 'class_labels': tensor([36,  4, 44, 52, 48]), 'boxes': tensor([[0.7891, 0.4437, 0.2094, 0.3562],\n         [0.3984, 0.6484, 0.3187, 0.3906],\n         [0.5891, 0.4070, 0.2219, 0.3859],\n         [0.3484, 0.2812, 0.2625, 0.4094],\n         [0.1602, 0.5023, 0.2672, 0.4109]]), 'area': tensor([17185.5000, 28687.5000, 19729.1250, 24759.0000, 25297.3125]), 'iscrowd': tensor([0, 0, 0, 0, 0]), 'orig_size': tensor([640, 640])}}\n

You have successfully augmented the images and prepared their annotations. In the final step, create a custom collate_fn to batch images together.

Python
def collate_fn(batch):\n    data = {}\n    data[\"pixel_values\"] = torch.stack([x[\"pixel_values\"] for x in batch])\n    data[\"labels\"] = [x[\"labels\"] for x in batch]\n    return data\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#preparing-function-to-compute-map","title":"Preparing function to compute mAP","text":"Python
id2label = {id: label for id, label in enumerate(ds_train.classes)}\nlabel2id = {label: id for id, label in enumerate(ds_train.classes)}\n\n\n@dataclass\nclass ModelOutput:\n    logits: torch.Tensor\n    pred_boxes: torch.Tensor\n\n\nclass MAPEvaluator:\n\n    def __init__(self, image_processor, threshold=0.00, id2label=None):\n        self.image_processor = image_processor\n        self.threshold = threshold\n        self.id2label = id2label\n\n    def collect_image_sizes(self, targets):\n        \"\"\"Collect image sizes across the dataset as list of tensors with shape [batch_size, 2].\"\"\"\n        image_sizes = []\n        for batch in targets:\n            batch_image_sizes = torch.tensor(np.array([x[\"size\"] for x in batch]))\n            image_sizes.append(batch_image_sizes)\n        return image_sizes\n\n    def collect_targets(self, targets, image_sizes):\n        post_processed_targets = []\n        for target_batch, image_size_batch in zip(targets, image_sizes):\n            for target, (height, width) in zip(target_batch, image_size_batch):\n                boxes = target[\"boxes\"]\n                boxes = sv.xcycwh_to_xyxy(boxes)\n                boxes = boxes * np.array([width, height, width, height])\n                boxes = torch.tensor(boxes)\n                labels = torch.tensor(target[\"class_labels\"])\n                post_processed_targets.append({\"boxes\": boxes, \"labels\": labels})\n        return post_processed_targets\n\n    def collect_predictions(self, predictions, image_sizes):\n        post_processed_predictions = []\n        for batch, target_sizes in zip(predictions, image_sizes):\n            batch_logits, batch_boxes = batch[1], batch[2]\n            output = ModelOutput(logits=torch.tensor(batch_logits), pred_boxes=torch.tensor(batch_boxes))\n            post_processed_output = self.image_processor.post_process_object_detection(\n                output, threshold=self.threshold, target_sizes=target_sizes\n            )\n            post_processed_predictions.extend(post_processed_output)\n        return post_processed_predictions\n\n    @torch.no_grad()\n    def __call__(self, evaluation_results):\n\n        predictions, targets = evaluation_results.predictions, evaluation_results.label_ids\n\n        image_sizes = self.collect_image_sizes(targets)\n        post_processed_targets = self.collect_targets(targets, image_sizes)\n        post_processed_predictions = self.collect_predictions(predictions, image_sizes)\n\n        evaluator = MeanAveragePrecision(box_format=\"xyxy\", class_metrics=True)\n        evaluator.warn_on_many_detections = False\n        evaluator.update(post_processed_predictions, post_processed_targets)\n\n        metrics = evaluator.compute()\n\n        # Replace list of per class metrics with separate metric for each class\n        classes = metrics.pop(\"classes\")\n        map_per_class = metrics.pop(\"map_per_class\")\n        mar_100_per_class = metrics.pop(\"mar_100_per_class\")\n        for class_id, class_map, class_mar in zip(classes, map_per_class, mar_100_per_class):\n            class_name = id2label[class_id.item()] if id2label is not None else class_id.item()\n            metrics[f\"map_{class_name}\"] = class_map\n            metrics[f\"mar_100_{class_name}\"] = class_mar\n\n        metrics = {k: round(v.item(), 4) for k, v in metrics.items()}\n\n        return metrics\n\neval_compute_metrics_fn = MAPEvaluator(image_processor=processor, threshold=0.01, id2label=id2label)\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#training-the-detection-model","title":"Training the detection model","text":"

You have done most of the heavy lifting in the previous sections, so now you are ready to train your model! The images in this dataset are still quite large, even after resizing. This means that finetuning this model will require at least one GPU.

Training involves the following steps:

  • Load the model with AutoModelForObjectDetection using the same checkpoint as in the preprocessing.
  • Define your training hyperparameters in TrainingArguments.
  • Pass the training arguments to Trainer along with the model, dataset, image processor, and data collator.
  • Call train() to finetune your model.

When loading the model from the same checkpoint that you used for the preprocessing, remember to pass the label2id and id2label maps that you created earlier from the dataset's metadata. Additionally, we specify ignore_mismatched_sizes=True to replace the existing classification head with a new one.

Python
model = AutoModelForObjectDetection.from_pretrained(\n    CHECKPOINT,\n    id2label=id2label,\n    label2id=label2id,\n    anchor_image_size=None,\n    ignore_mismatched_sizes=True,\n)\n
Some weights of RTDetrForObjectDetection were not initialized from the model checkpoint at PekingU/rtdetr_r50vd_coco_o365 and are newly initialized because the shapes did not match:\n- model.decoder.class_embed.0.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.0.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.1.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.1.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.2.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.2.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.3.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.3.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.4.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.4.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.5.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.5.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.denoising_class_embed.weight: found shape torch.Size([81, 256]) in the checkpoint and torch.Size([54, 256]) in the model instantiated\n- model.enc_score_head.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.enc_score_head.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\nYou should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n

In the TrainingArguments use output_dir to specify where to save your model, then configure hyperparameters as you see fit. For num_train_epochs=10 training will take about 15 minutes in Google Colab T4 GPU, increase the number of epoch to get better results.

Important notes:

  • Do not remove unused columns because this will drop the image column. Without the image column, you can't create pixel_values. For this reason, set remove_unused_columns to False.
  • Set eval_do_concat_batches=False to get proper evaluation results. Images have different number of target boxes, if batches are concatenated we will not be able to determine which boxes belongs to particular image.
Python
training_args = TrainingArguments(\n    output_dir=f\"{dataset.name.replace(' ', '-')}-finetune\",\n    num_train_epochs=20,\n    max_grad_norm=0.1,\n    learning_rate=5e-5,\n    warmup_steps=300,\n    per_device_train_batch_size=16,\n    dataloader_num_workers=2,\n    metric_for_best_model=\"eval_map\",\n    greater_is_better=True,\n    load_best_model_at_end=True,\n    eval_strategy=\"epoch\",\n    save_strategy=\"epoch\",\n    save_total_limit=2,\n    remove_unused_columns=False,\n    eval_do_concat_batches=False,\n)\n

Finally, bring everything together, and call train():

Python
trainer = Trainer(\n    model=model,\n    args=training_args,\n    train_dataset=pytorch_dataset_train,\n    eval_dataset=pytorch_dataset_valid,\n    tokenizer=processor,\n    data_collator=collate_fn,\n    compute_metrics=eval_compute_metrics_fn,\n)\n\ntrainer.train()\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#evaluate","title":"Evaluate","text":"Python
# @title Collect predictions\n\ntargets = []\npredictions = []\n\nfor i in range(len(ds_test)):\n    path, sourece_image, annotations = ds_test[i]\n\n    image = Image.open(path)\n    inputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\n    with torch.no_grad():\n        outputs = model(**inputs)\n\n    w, h = image.size\n    results = processor.post_process_object_detection(\n        outputs, target_sizes=[(h, w)], threshold=0.3)\n\n    detections = sv.Detections.from_transformers(results[0])\n\n    targets.append(annotations)\n    predictions.append(detections)\n
Python
# @title Calculate mAP\nmean_average_precision = sv.MeanAveragePrecision.from_detections(\n    predictions=predictions,\n    targets=targets,\n)\n\nprint(f\"map50_95: {mean_average_precision.map50_95:.2f}\")\nprint(f\"map50: {mean_average_precision.map50:.2f}\")\nprint(f\"map75: {mean_average_precision.map75:.2f}\")\n
map50_95: 0.89\nmap50: 0.94\nmap75: 0.94\n
Python
# @title Calculate Confusion Matrix\nconfusion_matrix = sv.ConfusionMatrix.from_detections(\n    predictions=predictions,\n    targets=targets,\n    classes=ds_test.classes\n)\n\n_ = confusion_matrix.plot()\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#save-fine-tuned-model-on-hard-drive","title":"Save fine-tuned model on hard drive","text":"Python
model.save_pretrained(\"/content/rt-detr/\")\nprocessor.save_pretrained(\"/content/rt-detr/\")\n
['/content/rt-detr/preprocessor_config.json']\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#inference-with-fine-tuned-rt-detr-model","title":"Inference with fine-tuned RT-DETR model","text":"Python
IMAGE_COUNT = 5\n\nfor i in range(IMAGE_COUNT):\n    path, sourece_image, annotations = ds_test[i]\n\n    image = Image.open(path)\n    inputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\n    with torch.no_grad():\n        outputs = model(**inputs)\n\n    w, h = image.size\n    results = processor.post_process_object_detection(\n        outputs, target_sizes=[(h, w)], threshold=0.3)\n\n    detections = sv.Detections.from_transformers(results[0]).with_nms(threshold=0.1)\n\n    annotated_images = [\n        annotate(sourece_image, annotations, ds_train.classes),\n        annotate(sourece_image, detections, ds_train.classes)\n    ]\n    grid = sv.create_tiles(\n        annotated_images,\n        titles=['ground truth', 'prediction'],\n        titles_scale=0.5,\n        single_tile_size=(400, 400),\n        tile_padding_color=sv.Color.WHITE,\n        tile_margin_color=sv.Color.WHITE\n    )\n    sv.plot_image(grid, size=(6, 6))\n
"},{"location":"introduction/image_augmentation/","title":"What is image augmentation and how it can improve the performance of deep neural networks","text":"

Deep neural networks require a lot of training data to obtain good results and prevent overfitting. However, it is often very difficult to get enough training samples. Multiple reasons could make it very hard or even impossible to gather enough data:

  • To make a training dataset, you need to obtain images and then label them. For example, you need to assign correct class labels if you have an image classification task. For an object detection task, you need to draw bounding boxes around objects. For a semantic segmentation task, you need to assign a correct class to each input image pixel. This process requires manual labor, and sometimes it could be very costly to label the training data. For example, to correctly label medical images, you need expensive domain experts.

  • Sometimes even collecting training images could be hard. There are many legal restrictions for working with healthcare data, and obtaining it requires a lot of effort. Sometimes getting the training images is more feasible, but it will cost a lot of money. For example, to get satellite images, you need to pay a satellite operator to take those photos. To get images for road scene recognition, you need an operator that will drive a car and collect the required data.

"},{"location":"introduction/image_augmentation/#image-augmentation-to-the-rescue","title":"Image augmentation to the rescue","text":"

Image augmentation is a process of creating new training examples from the existing ones. To make a new sample, you slightly change the original image. For instance, you could make a new image a little brighter; you could cut a piece from the original image; you could make a new image by mirroring the original one, etc.

Here are some examples of transformations of the original image that will create a new training sample.

By applying those transformations to the original training dataset, you could create an almost infinite amount of new training samples.

"},{"location":"introduction/image_augmentation/#how-much-does-image-augmentation-improves-the-quality-and-performance-of-deep-neural-networks","title":"How much does image augmentation improves the quality and performance of deep neural networks","text":"

Basic augmentations techniques were used almost in all papers that describe the state-of-the-art models for image recognition.

AlexNet was the first model that demonstrated exceptional capabilities of using deep neural networks for image recognition. For training, the authors used a set of basic image augmentation techniques. They resized original images to the fixed size of 256 by 256 pixels, and then they cropped patches of size 224 by 224 pixels as well as their horizontal reflections from those resized images. Also, they altered the intensities of the RGB channels in images.

Successive state-of-the-art models such as Inception, ResNet, and EfficientNet also used image augmentation techniques for training.

In 2018 Google published a paper about AutoAugment - an algorithm that automatically discovers the best set of augmentations for the dataset. They showed that a custom set of augmentations improves the performance of the model.

Here is a comparison between a model that used only the base set of augmentations and a model that used a specific set of augmentations discovered by AutoAugment. The table shows Top-1 accuracy (%) on the ImageNet validation set; higher is better.

Model Base augmentations AutoAugment augmentations ResNet-50 76.3 77.6 ResNet-200 78.5 80.0 AmoebaNet-B (6,190) 82.2 82.8 AmoebaNet-C (6,228) 83.1 83.5

The table demonstrates that a diverse set of image augmentations improves the performance of neural networks compared to a base set with only a few most popular transformation techniques.

Augmentations help to fight overfitting and improve the performance of deep neural networks for computer vision tasks such as classification, segmentation, and object detection. The best part is that image augmentations libraries such as Albumentations make it possible to add image augmentations to any computer vision pipeline with minimal effort.

"},{"location":"introduction/why_albumentations/","title":"Why Albumentations","text":""},{"location":"introduction/why_albumentations/#a-single-interface-to-work-with-images-masks-bounding-boxes-and-key-points","title":"A single interface to work with images, masks, bounding boxes, and key points","text":"

Albumentations provides a single interface to work with different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, pose estimation, etc.

"},{"location":"introduction/why_albumentations/#battle-tested","title":"Battle-tested","text":"

The library is widely used in industry, deep learning research, machine learning competitions, and open source projects.

"},{"location":"introduction/why_albumentations/#high-performance","title":"High performance","text":"

Albumentations optimized for maximum speed and performance. Under the hood, the library uses highly optimized functions from OpenCV and NumPy for data processing. We have a regularly updated benchmark that compares the speed of popular image augmentations libraries for the most common image transformations. Albumentations demonstrates the best performance in most cases.

"},{"location":"introduction/why_albumentations/#diverse-set-of-supported-augmentations","title":"Diverse set of supported augmentations","text":"

Albumentations supports more than 60 different image augmentations.

"},{"location":"introduction/why_albumentations/#extensibility","title":"Extensibility","text":"

Albumentations allows to easily add new augmentations and use them in computer vision pipelines through a single interface along with built-in transformations.

"},{"location":"introduction/why_albumentations/#rigorous-testing","title":"Rigorous testing","text":"

Bugs in the augmentation pipeline could silently corrupt the input data. They can easily go unnoticed, but the performance of the models trained with incorrect data will degrade. Albumentations has an extensive test suite that helps to discover bugs during development.

"},{"location":"introduction/why_albumentations/#it-is-open-source-and-mit-licensed","title":"It is open source and MIT licensed","text":"

You can find the source code on GitHub.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/","title":"Why you need a dedicated library for image augmentation","text":"

At first glance, image augmentations look very simple; you apply basic transformations to an image: mirroring, cropping, changing brightness and contrast, etc.

There are a lot of libraries that could do such image transformations. Here is an example of how you could use Pillow, a popular image processing library for Python, to make simple augmentations.

Python
from PIL import Image, ImageEnhance\n\nimage = Image.open(\"parrot.jpg\")\n\nmirrored_image = image.transpose(Image.FLIP_LEFT_RIGHT)\n\nrotated_image = image.rotate(45)\n\nbrightness_enhancer = ImageEnhance.Brightness(image)\nbrighter_image = brightness_enhancer.enhance(factor=1.5)\n

However, this approach has many limitations, and it doesn't handle all cases with image augmentation. An image augmentation library such as Albumentations gives you a lot of advantages.

Here is a list of few pitfalls that augmentation libraries can handle very well.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#the-need-to-apply-the-same-transform-to-an-image-and-for-labels-for-segmentation-object-detection-and-keypoint-detection-tasks","title":"The need to apply the same transform to an image and for labels for segmentation, object detection, and keypoint detection tasks.","text":"

For image classification, you need to modify only an input image and keep output labels intact because output labels are invariant to image modifications.

Note

There are some exceptions to this rule. For example, an image could contain a cat and have an assigned label cat. During image augmentation, if you crop a part of an image that doesn't have a cat on it, then the output label cat becomes wrong and misleading. Usually, you deal with those situations by deciding which augmentations you could apply to a dataset without risking to have problems with incorrect labels.

For segmentation, you need to apply some transformations both to an input image and an output mask. You also have to use the same parameters both for the image transformation and the mask transformation.

Let's look at an example of a semantic segmentation task from Inria Aerial Image Labeling Dataset. The dataset contains aerial photos as well as masks for those photos. Each pixel of the mask is marked either as 1 if the pixel belongs to the class building and 0 otherwise.

There are two types of image augmentations: pixel-level augmentations and spatial-level augmentations.

Pixel-level augmentations change the values of pixels of the original image, but they don't change the output mask. Image transformations such as changing brightness or contrast of adjusting values of the RGB-palette of the image are pixel-level augmentations.

We modify the input image by adjusting its brightness, but we keep the output mask intact.

On the contrary, spatial-level augmentations change both the image and the mask. When you apply image transformations such as mirroring or rotation or cropping a part of the input image, you also need to apply the same transformation to the output label to preserve its correctness.

We rotate both the input image and the output mask. We use the same set of transformations with the same parameters, both for the image and the mask.

The same is true for object detection tasks. For pixel-level augmentations, you only need to change the input image. With spatial-level augmentations, you need to apply the same transformation not only to the image but for bounding boxes coordinates as well. After applying spatial-level augmentations, you need to update coordinates of bounding boxes to represent the correct locations of objects on the augmented image.

Pixel-level augmentations such as brightness adjustment change only the input image but not the coordinates of bounding boxes. Spatial-level augmentations such as mirroring and cropping a part of the image change both the input image and the bounding boxes' coordinates.

Albumentations knows how to correctly apply transformation both to the input data as well as the output labels.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#working-with-probabilities","title":"Working with probabilities","text":"

During training, you usually want to apply augmentations with a probability of less than 100% since you also need to have the original images in your training pipeline. Also, it is beneficial to be able to control the magnitude of image augmentation, how much does the augmentation change the original image. If the original dataset is large, you could apply only the basic augmentations with probability around 10-30% and with a small magnitude of changes. If the dataset is small, you need to act more aggressively with augmentations to prevent overfitting of neural networks, so you usually need to increase the probability of applying each augmentation to 40-50% and increase the magnitude of changes the augmentation makes to the image.

Image augmentation libraries allow you to set the required probabilities and the magnitude of values for each transformation.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#declarative-definition-of-the-augmentation-pipeline-and-unified-interface","title":"Declarative definition of the augmentation pipeline and unified interface","text":"

Usually, you want to apply not a single augmentation, but a set of augmentations with specific parameters such as probability and magnitude of changes. Augmentation libraries allow you to declare such a pipeline in a single place and then use it for image transformation through a unified interface. Some libraries can store and load transformation parameters to formats such as JSON, YAML, etc.

Here is an example definition of an augmentation pipeline. This pipeline will first crop a random 512px x 512px part of the input image. Then with probability 30%, it will randomly change brightness and contrast of that crop. Finally, with probability 50%, it will horizontally flip the resulting image.

Python
import albumentations as A\n\ntransform = A.Compose([\n    A.RandomCrop(512, 512),\n    A.RandomBrightnessContrast(p=0.3),\n    A.HorizontalFlip(p=0.5),\n])\n
"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#rigorous-testing","title":"Rigorous testing","text":"

A bug in the augmentation pipeline could easily go unnoticed. A buggy pipeline could silently corrupt input data. There won't be any exceptions and code failures, but the performance of trained neural networks will degrade because they received a garbage input during training. Augmentation libraries usually have large test suites that capture regressions during development. Also large user base helps to find unnoticed bugs and report them to developers.

"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Albumentations documentation","text":"

Albumentations is a fast and flexible image augmentation library. The library is widely used in industry, deep learning research, machine learning competitions, and open source projects. Albumentations is written in Python, and it is licensed under the MIT license. The source code is available at https://github.com/albumentations-team/albumentations.

If you are new to image augmentation, start with our \"Learning Path\" for beginners. It describes what image augmentation is, how it can boost deep neural networks' performance, and why you should use Albumentations.

For hands-on experience, check out our \"Quick Start Guide\" and \"Examples\" sections. They show how you can use the library for different computer vision tasks: image classification, semantic segmentation, instance segmentation, object detection, and keypoint detection. Each example includes a link to Google Colab, where you can run the code by yourself.

You can also visit explore.albumentations.ai to visually explore and experiment with different augmentations in your browser. This interactive tool helps you better understand how each transform affects images before implementing it in your code.

\"API Reference\" contains the description of Albumentations' methods and classes.

"},{"location":"#quick-start-guide","title":"Quick Start Guide","text":"
  • Installation
  • Frequently Asked Questions
  • Your First Augmentation Pipeline
"},{"location":"#working-with-multi-dimensional-data","title":"Working with Multi-dimensional Data","text":""},{"location":"#volumetric-data-3d","title":"Volumetric Data (3D)","text":"
  • Introduction to 3D (Volumetric) Image Augmentation
  • Available 3D Transforms
"},{"location":"#video-and-sequential-data","title":"Video and Sequential Data","text":"
  • Video Frame Augmentation
"},{"location":"#learning-path","title":"Learning Path","text":""},{"location":"#beginners","title":"Beginners","text":"
  • What is Image Augmentation?
  • Why Choose Albumentations?
  • Basic Image Classification
"},{"location":"#intermediate","title":"Intermediate","text":"
  • Semantic Segmentation
  • Object Detection
  • Keypoint Detection
  • Multi-target Augmentation
"},{"location":"#advanced","title":"Advanced","text":"
  • Pipeline Configuration
  • Debugging with ReplayCompose
  • Serialization
"},{"location":"#framework-integration","title":"Framework Integration","text":"
  • PyTorch
  • TensorFlow
  • HuggingFace
  • Roboflow
  • Voxel51
"},{"location":"#library-comparisons","title":"Library Comparisons","text":"
  • Transform Library Comparison - Find equivalent transforms between Albumentations and other libraries (torchvision, Kornia)
  • Migration from torchvision - Step-by-step migration guide
"},{"location":"#examples","title":"Examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Using Albumentations for a semantic segmentation task
  • Using Albumentations to augment keypoints
  • Applying the same augmentation with the same parameters to multiple images, masks, bounding boxes, or keypoints
  • Weather augmentations in Albumentations
  • Example of applying XYMasking transform
  • Example of applying ChromaticAberration transform
  • Example of applying Morphological transform
  • Example of applying D4 transform
  • Example of applying RandomGridShuffle transform
  • Example of applying OverlayElements transform
  • Example of applying TextImage transform
  • Debugging an augmentation pipeline with ReplayCompose
  • How to save and load parameters of an augmentation pipeline
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
  • How to save and load transforms to HuggingFace Hub.
"},{"location":"#examples-of-how-to-use-albumentations-with-different-deep-learning-frameworks","title":"Examples of how to use Albumentations with different deep learning frameworks","text":"
  • PyTorch and Albumentations for image classification
  • PyTorch and Albumentations for semantic segmentation
  • Using Albumentations with Tensorflow
"},{"location":"#external-resources","title":"External resources","text":"
  • Blog posts, podcasts, talks, and videos about Albumentations
  • Books that mention Albumentations
  • Online courses that cover Albumentations
"},{"location":"#other-topics","title":"Other topics","text":"
  • Contributing
"},{"location":"#api-reference","title":"API Reference","text":"
  • Full API Reference on a single page
  • Index
  • Core API (albumentations.core)
  • Augmentations (albumentations.augmentations)
  • PyTorch Helpers (albumentations.pytorch)
"},{"location":"CONTRIBUTING/","title":"Contributing to Albumentations","text":"

Thank you for your interest in contributing to Albumentations! This guide will help you get started with contributing to our image augmentation library.

"},{"location":"CONTRIBUTING/#quick-start","title":"Quick Start","text":"

For small changes (e.g., bug fixes), feel free to submit a PR directly.

For larger changes:

  1. Create an issue outlining your proposed change
  2. Join our Discord community to discuss your idea
"},{"location":"CONTRIBUTING/#contribution-guides","title":"Contribution Guides","text":"

We've organized our contribution guidelines into focused documents:

  • Environment Setup Guide - How to set up your development environment
  • Coding Guidelines - Code style, best practices, and technical requirements
"},{"location":"CONTRIBUTING/#contribution-process","title":"Contribution Process","text":"
  1. Find an Issue: Look for open issues or propose a new one. For newcomers, look for issues labeled \"good first issue\"
  2. Set Up: Follow our Environment Setup Guide
  3. Create a Branch: git checkout -b feature/my-new-feature
  4. Make Changes: Write code following our Coding Guidelines
  5. Test: Add tests and ensure all tests pass
  6. Submit: Open a Pull Request with a clear description of your changes
"},{"location":"CONTRIBUTING/#code-review-process","title":"Code Review Process","text":"
  1. Maintainers will review your contribution
  2. Address any feedback or questions
  3. Once approved, your code will be merged
"},{"location":"CONTRIBUTING/#project-structure","title":"Project Structure","text":"
  • albumentations/ - Main source code
  • tests/ - Test suite
  • docs/ - Documentation
"},{"location":"CONTRIBUTING/#getting-help","title":"Getting Help","text":"
  • Join our Discord community
  • Open a GitHub issue
  • Ask questions in your pull request
"},{"location":"CONTRIBUTING/#license","title":"License","text":"

By contributing, you agree that your contributions will be licensed under the project's MIT License.

"},{"location":"benchmarking_results/","title":"Benchmarking results","text":""},{"location":"benchmarking_results/#benchmarking-results_1","title":"Benchmarking results","text":""},{"location":"benchmarking_results/#system-information","title":"System Information","text":"
  • Platform: macOS-15.0.1-arm64-arm-64bit
  • Processor: arm
  • CPU Count: 10
  • Python Version: 3.12.7
"},{"location":"benchmarking_results/#benchmark-parameters","title":"Benchmark Parameters","text":"
  • Number of images: 1000
  • Runs per transform: 10
  • Max warmup iterations: 1000
"},{"location":"benchmarking_results/#library-versions","title":"Library Versions","text":"
  • albumentations: 1.4.20
  • augly: 1.0.0
  • imgaug: 0.4.0
  • kornia: 0.7.3
  • torchvision: 0.20.0
"},{"location":"benchmarking_results/#performance-comparison","title":"Performance Comparison","text":"

Number - is the number of uint8 RGB images processed per second on a single CPU core. Higher is better.

Transform albumentations1.4.20 augly1.0.0 imgaug0.4.0 kornia0.7.3 torchvision0.20.0 HorizontalFlip 8618 \u00b1 1233 4807 \u00b1 818 6042 \u00b1 788 390 \u00b1 106 914 \u00b1 67 VerticalFlip 22847 \u00b1 2031 9153 \u00b1 1291 10931 \u00b1 1844 1212 \u00b1 402 3198 \u00b1 200 Rotate 1146 \u00b1 79 1119 \u00b1 41 1136 \u00b1 218 143 \u00b1 11 181 \u00b1 11 Affine 682 \u00b1 192 - 774 \u00b1 97 147 \u00b1 9 130 \u00b1 12 Equalize 892 \u00b1 61 - 581 \u00b1 54 152 \u00b1 19 479 \u00b1 12 RandomCrop80 47341 \u00b1 20523 25272 \u00b1 1822 11503 \u00b1 441 1510 \u00b1 230 32109 \u00b1 1241 ShiftRGB 2349 \u00b1 76 - 1582 \u00b1 65 - - Resize 2316 \u00b1 166 611 \u00b1 78 1806 \u00b1 63 232 \u00b1 24 195 \u00b1 4 RandomGamma 8675 \u00b1 274 - 2318 \u00b1 269 108 \u00b1 13 - Grayscale 3056 \u00b1 47 2720 \u00b1 932 1681 \u00b1 156 289 \u00b1 75 1838 \u00b1 130 RandomPerspective 412 \u00b1 38 - 554 \u00b1 22 86 \u00b1 11 96 \u00b1 5 GaussianBlur 1728 \u00b1 89 242 \u00b1 4 1090 \u00b1 65 176 \u00b1 18 79 \u00b1 3 MedianBlur 868 \u00b1 60 - 813 \u00b1 30 5 \u00b1 0 - MotionBlur 4047 \u00b1 67 - 612 \u00b1 18 73 \u00b1 2 - Posterize 9094 \u00b1 301 - 2097 \u00b1 68 430 \u00b1 49 3196 \u00b1 185 JpegCompression 918 \u00b1 23 778 \u00b1 5 459 \u00b1 35 71 \u00b1 3 625 \u00b1 17 GaussianNoise 166 \u00b1 12 67 \u00b1 2 206 \u00b1 11 75 \u00b1 1 - Elastic 201 \u00b1 5 - 235 \u00b1 20 1 \u00b1 0 2 \u00b1 0 Clahe 454 \u00b1 22 - 335 \u00b1 43 94 \u00b1 9 - CoarseDropout 13368 \u00b1 744 - 671 \u00b1 38 536 \u00b1 87 - Blur 5267 \u00b1 543 246 \u00b1 3 3807 \u00b1 325 - - ColorJitter 628 \u00b1 55 255 \u00b1 13 - 55 \u00b1 18 46 \u00b1 2 Brightness 8956 \u00b1 300 1163 \u00b1 86 - 472 \u00b1 101 429 \u00b1 20 Contrast 8879 \u00b1 1426 736 \u00b1 79 - 425 \u00b1 52 335 \u00b1 35 RandomResizedCrop 2828 \u00b1 186 - - 287 \u00b1 58 511 \u00b1 10 Normalize 1196 \u00b1 56 - - 626 \u00b1 40 519 \u00b1 12 PlankianJitter 2204 \u00b1 385 - - 813 \u00b1 211 -"},{"location":"faq/","title":"Frequently Asked Questions","text":"

This FAQ covers common questions about Albumentations, from basic setup to advanced usage. You'll find information about:

  • Installation troubleshooting and configuration
  • Working with different data formats (images, video, volumetric data)
  • Advanced usage patterns and best practices
  • Integration with other tools and migration from other libraries

If you don't find an answer to your question, please check our GitHub Issues or join our Discord community.

"},{"location":"faq/#installation","title":"Installation","text":""},{"location":"faq/#i-am-receiving-an-error-message-failed-building-wheel-for-imagecodecs-when-i-am-trying-to-install-albumentations-how-can-i-fix-the-problem","title":"I am receiving an error message Failed building wheel for imagecodecs when I am trying to install Albumentations. How can I fix the problem?","text":"

Try to update pip by running the following command:

Bash
python -m pip install --upgrade pip\n
"},{"location":"faq/#how-to-disable-automatic-checks-for-new-versions","title":"How to disable automatic checks for new versions?","text":"

To disable automatic checks for new versions, set the environment variable NO_ALBUMENTATIONS_UPDATE to 1.

"},{"location":"faq/#how-to-make-albumentations-use-one-cpu-core","title":"How to make Albumentations use one CPU core?","text":"

Albumentations do not use multithreading by default, but libraries it depends on (like opencv) may use multithreading. To make Albumentations use one CPU core, you can set the following environment variables:

Python
os.environ[\"OMP_NUM_THREADS\"] = \"1\"\nos.environ[\"OPENBLAS_NUM_THREADS\"] = \"1\"\nos.environ[\"MKL_NUM_THREADS\"] = \"1\"\nos.environ[\"VECLIB_MAXIMUM_THREADS\"] = \"1\"\nos.environ[\"NUMEXPR_NUM_THREADS\"] = \"1\"\n
"},{"location":"faq/#data-formats-and-basic-usage","title":"Data Formats and Basic Usage","text":""},{"location":"faq/#supported-image-types","title":"Supported Image Types","text":"

Albumentations works with images of type uint8 and float32. uint8 images should be in the [0, 255] range, and float32 images should be in the [0, 1] range. If float32 images lie outside of the [0, 1] range, they will be automatically clipped to the [0, 1] range.

"},{"location":"faq/#why-do-you-call-cv2cvtcolorimage-cv2color_bgr2rgb-in-your-examples","title":"Why do you call cv2.cvtColor(image, cv2.COLOR_BGR2RGB) in your examples?","text":"

For historical reasons, OpenCV reads an image in BGR format (so color channels of the image have the following order: Blue, Green, Red). Albumentations uses the most common and popular RGB image format. So when using OpenCV, we need to convert the image format to RGB explicitly.

"},{"location":"faq/#how-to-have-reproducible-augmentations","title":"How to have reproducible augmentations?","text":"

To have reproducible augmentations, set the seed parameter in your transform pipeline. This will ensure that the same random parameters are used for each augmentation, resulting in the same output for the same input.

Python
transform = A.Compose([\n    A.RandomCrop(height=256, width=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], seed=42)\n
"},{"location":"faq/#working-with-different-data-types","title":"Working with Different Data Types","text":""},{"location":"faq/#how-to-process-video-data-with-albumentations","title":"How to process video data with Albumentations?","text":"

Albumentations can process video data by treating it as a sequence of frames in numpy array format: - (N, H, W) - Grayscale video (N frames) - (N, H, W, C) - Color video (N frames)

When you pass a video array, Albumentations will apply the same transform with identical parameters to each frame, ensuring temporal consistency.

Python
video = np.random.rand(32, 256, 256, 3) # 32 RGB frames\n\ntransform = A.Compose([\n  A.RandomCrop(height=224, width=224),\n  A.HorizontalFlip(p=0.5)\n], seed=42)\n\ntransformed = transform(image=video)['image']\n

See Working with Video Data for more info.

"},{"location":"faq/#how-to-process-volumetric-data-with-albumentations","title":"How to process volumetric data with Albumentations?","text":"

Albumentations can process volumetric data by treating it as a sequence of 2D slices. When you pass a volumetric data as a numpy array, Albumentations will apply the same transform with identical parameters to each slice, ensuring temporal consistency.

See Working with Volumetric Data (3D) for more info.

"},{"location":"faq/#my-computer-vision-pipeline-works-with-a-sequence-of-images-i-want-to-apply-the-same-augmentations-with-the-same-parameters-to-each-image-in-the-sequence-can-albumentations-do-it","title":"My computer vision pipeline works with a sequence of images. I want to apply the same augmentations with the same parameters to each image in the sequence. Can Albumentations do it?","text":"

Yes. You can define additional images, masks, bounding boxes, or keypoints through the additional_targets argument to Compose. You can then pass those additional targets to the augmentation pipeline, and Albumentations will augment them in the same way. See this example for more info.

But if you want only to the sequence of images, you may just use images target that accepts list[numpy.ndarray] or np.ndarray with shape (N, H, W, C) / (N, H, W).

"},{"location":"faq/#advanced-usage","title":"Advanced Usage","text":""},{"location":"faq/#how-can-i-find-which-augmentations-were-applied-to-the-input-data-and-which-parameters-they-used","title":"How can I find which augmentations were applied to the input data and which parameters they used?","text":"

You may pass save_applied_params=True to Compose to save the parameters of the applied augmentations. You can access them later using applied_transforms.

Python
transform = A.Compose([\n    A.RandomCrop(256, 256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.5),\n    A.RandomGamma(p=0.5),\n    A.Normalize(),\n], save_applied_params=True, seed=42)\n\ntransformed = transform(image=image)['image']\n\nprint(transform[\"applied_transforms\"])\n
"},{"location":"faq/#how-to-perform-balanced-scaling","title":"How to perform balanced scaling?","text":"

The default scaling logic in RandomScale, ShiftScaleRotate, and Affine transformations is biased towards upscaling.

For example, if scale_limit = (0.5, 2), a user might expect that the image will be scaled down in half of the cases and scaled up in the other half. However, in reality, the image will be scaled up in 75% of the cases and scaled down in only 25% of the cases. This is because the default behavior samples uniformly from the interval [0.5, 2], and the interval [0.5, 1] is three times smaller than [1, 2].

To achieve balanced scaling, you can use Affine with balanced_scale=True, which ensures that the probability of scaling up and scaling down is equal.

Python
balanced_scale_transform = A.Affine(scale=(0.5, 2), balanced_scale=True)\n

or use OneOf transform as follows:

Python
balanced_scale_transform = A.OneOf([\n  A.Affine(scale=(0.5, 1), p=0.5),\n  A.Affine(scale=(1, 2), p=0.5)])\n

This approach ensures that exactly half of the samples will be upscaled and half will be downscaled.

"},{"location":"faq/#augmentations-have-a-parameter-named-p-that-sets-the-probability-of-applying-that-augmentation-how-does-p-work-in-nested-containers","title":"Augmentations have a parameter named p that sets the probability of applying that augmentation. How does p work in nested containers?","text":"

The p parameter sets the probability of applying a specific augmentation. When augmentations are nested within a top-level container like Compose, the effective probability of each augmentation is the product of the container's probability and the augmentation's probability.

Let's look at an example when a container Compose contains one augmentation Resize:

Python
transform = A.Compose([\n    A.Resize(height=256, width=256, p=1.0),\n], p=0.9)\n

In this case, Resize has a 90% chance to be applied. This is because there is a 90% chance for Compose to be applied (p=0.9). If Compose is applied, then Resize is applied with 100% probability (p=1.0).

To visualize:

  • Probability of Compose being applied: 0.9
  • Probability of Resize being applied given Compose is applied: 1.0
  • Effective probability of Resize being applied: 0.9 * 1.0 = 0.9 (or 90%)

This means that the effective probability of Resize being applied is the product of the probabilities of Compose and Resize, which is 0.9 * 1.0 = 0.9 or 90%. This principle applies to other transformations as well, where the overall probability is the product of the individual probabilities within the transformation pipeline.

Here\u2019s another example:

Python
transform = A.Compose([\n    A.Resize(height=256, width=256, p=0.5),\n], p=0.9)\n

In this example, Resize has an effective probability of being applied as 0.9 * 0.5 = 0.45 or 45%. This is because Compose is applied 90% of the time, and within that 90%, Resize is applied 50% of the time.

"},{"location":"faq/#i-created-annotations-for-bounding-boxes-using-labeling-service-or-labeling-software-how-can-i-use-those-annotations-in-albumentations","title":"I created annotations for bounding boxes using labeling service or labeling software. How can I use those annotations in Albumentations?","text":"

You need to convert those annotations to one of the formats, supported by Albumentations. For the list of formats, please refer to this article. Consult the documentation of the labeling service to see how you can export annotations in those formats.

"},{"location":"faq/#integration-and-migration","title":"Integration and Migration","text":""},{"location":"faq/#how-to-save-and-load-augmentation-transforms-to-huggingface-hub","title":"How to save and load augmentation transforms to HuggingFace Hub?","text":"Python
import albumentations as A\nimport numpy as np\n\ntransform = A.Compose([\n    A.RandomCrop(256, 256),\n    A.HorizontalFlip(),\n    A.RandomBrightnessContrast(),\n    A.RGBShift(),\n    A.Normalize(),\n])\n\ntransform.save_pretrained(\"qubvel-hf/albu\", key=\"train\")\n# The 'key' parameter specifies the context or purpose of the saved transform,\n# allowing for organized and context-specific retrieval.\n# ^ this will save the transform to a directory \"qubvel-hf/albu\" with filename \"albumentations_config_train.json\"\n\ntransform.save_pretrained(\"qubvel-hf/albu\", key=\"train\", push_to_hub=True)\n# ^ this will save the transform to a directory \"qubvel-hf/albu\" with filename \"albumentations_config_train.json\"\n# + push the transform to the Hub to the repository \"qubvel-hf/albu\"\n\ntransform.push_to_hub(\"qubvel-hf/albu\", key=\"train\")\n# Use `save_pretrained` to save the transform locally and optionally push to the Hub.\n# Use `push_to_hub` to directly push the transform to the Hub without saving it locally.\n# ^ this will push the transform to the Hub to the repository \"qubvel-hf/albu\" (without saving it locally)\n\nloaded_transform = A.Compose.from_pretrained(\"qubvel-hf/albu\", key=\"train\")\n# ^ this will load the transform from local folder if exist or from the Hub repository \"qubvel-hf/albu\"\n

See this example for more info.

"},{"location":"faq/#how-do-i-migrate-from-other-augmentation-libraries-to-albumentations","title":"How do I migrate from other augmentation libraries to Albumentations?","text":"

If you're migrating from other libraries like torchvision or Kornia, you can refer to our Library Comparison & Benchmarks guide. This guide provides:

  1. Mapping tables showing equivalent transforms between libraries
  2. Performance benchmarks demonstrating Albumentations' speed advantages
  3. Code examples for common migration scenarios
  4. Key differences in implementation and parameter handling

For a quick visual comparison of different augmentations, you can also use our interactive tool at explore.albumentations.ai to see how transforms affect images before implementing them.

For specific migration examples, see:

  • Migrating from torchvision
  • Performance comparison with other libraries
"},{"location":"frameworks_and_libraries/","title":"Frameworks and libraries that use Albumentations","text":""},{"location":"frameworks_and_libraries/#mmdetection","title":"MMDetection","text":"

https://github.com/open-mmlab/mmdetection

MMDetection is an open source object detection toolbox based on PyTorch. It is a part of the OpenMMLab project.

  • To install MMDetection with Albumentations follow the installation instructions.
  • MMDetection has an example config with augmentations from Albumentations.
"},{"location":"frameworks_and_libraries/#yolov5","title":"YOLOv5","text":"

https://github.com/ultralytics/yolov5

YOLOv5 \ud83d\ude80 is a family of object detection architectures and models pretrained on the COCO dataset, and represents Ultralytics open-source research into future vision AI methods, incorporating lessons learned and best practices evolved over thousands of hours of research and development.

  • To use Albumentations along with YOLOv5 simply pip install -U albumentations and then update the augmentation pipeline as you see fit in the Albumentations class in utils/augmentations.py. An example is available in the YOLOv5 repository.
"},{"location":"frameworks_and_libraries/#other-frameworks-and-libraries","title":"Other frameworks and libraries","text":"

Other you can see find at GitHub

"},{"location":"api_reference/","title":"Index","text":"
  • Full API Reference on a single page
  • Core API (albumentations.core)
    • Composition API (albumentations.core.composition)
    • Serialization API (albumentations.core.serialization)
    • Transforms Interface (albumentations.core.transforms_interface)
    • Helper functions for working with bounding boxes (albumentations.core.bbox_utils)
    • Helper functions for working with keypoints (albumentations.core.keypoints_utils)
  • Augmentations (albumentations.augmentations)
    • Transforms (albumentations.augmentations.transforms)
    • Functional transforms (albumentations.augmentations.functional)
  • PyTorch Helpers (albumentations.pytorch)
    • Transforms (albumentations.pytorch.transforms)
"},{"location":"api_reference/full_reference/","title":"Full API Reference on a single page","text":""},{"location":"api_reference/full_reference/#transform-types","title":"Transform Types","text":""},{"location":"api_reference/full_reference/#1-pixel-level-transforms","title":"1. Pixel-level transforms","text":"

Transforms that modify pixel values without changing spatial relationships. These can be safely applied to any target as they only affect the input image, leaving other targets (masks, bounding boxes, keypoints) unchanged.

  • AdditiveNoise
  • AdvancedBlur
  • AutoContrast
  • Blur
  • CLAHE
  • ChannelDropout
  • ChannelShuffle
  • ChromaticAberration
  • ColorJitter
  • Defocus
  • Downscale
  • Emboss
  • Equalize
  • FDA
  • FancyPCA
  • FromFloat
  • GaussNoise
  • GaussianBlur
  • GlassBlur
  • HistogramMatching
  • HueSaturationValue
  • ISONoise
  • Illumination
  • ImageCompression
  • InvertImg
  • MedianBlur
  • MotionBlur
  • MultiplicativeNoise
  • Normalize
  • PixelDistributionAdaptation
  • PlanckianJitter
  • PlasmaBrightnessContrast
  • PlasmaShadow
  • Posterize
  • RGBShift
  • RandomBrightnessContrast
  • RandomFog
  • RandomGamma
  • RandomGravel
  • RandomRain
  • RandomShadow
  • RandomSnow
  • RandomSunFlare
  • RandomToneCurve
  • RingingOvershoot
  • SaltAndPepper
  • Sharpen
  • ShotNoise
  • Solarize
  • Spatter
  • Superpixels
  • TemplateTransform
  • TextImage
  • ToFloat
  • ToGray
  • ToRGB
  • ToSepia
  • UnsharpMask
  • ZoomBlur
"},{"location":"api_reference/full_reference/#2-spatial-level-transforms","title":"2. Spatial-level transforms","text":"

Transforms that modify the spatial arrangement of pixels/features. Different targets have different spatial transform support - see the compatibility table below:

Transform Image Mask BBoxes Keypoints Volume Mask3D Affine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 AtLeastOneBBoxRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 BBoxSafeRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CenterCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CoarseDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Crop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropAndPad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropNonEmptyMaskIfExists \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 D4 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ElasticTransform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Erasing \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 FrequencyMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridElasticDeform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 HorizontalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Lambda \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 LongestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 MaskDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Morphological \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 NoOp \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OpticalDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OverlayElements \u2713 \u2713 Pad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PadIfNeeded \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Perspective \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PiecewiseAffine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PixelDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropFromBorders \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropNearBBox \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomGridShuffle \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomResizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomRotate90 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomScale \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedBBoxSafeCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Resize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Rotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SafeRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ShiftScaleRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SmallestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ThinPlateSpline \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeReverse \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Transpose \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 VerticalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 XYMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713"},{"location":"api_reference/full_reference/#3-volumetric-3d-transforms","title":"3. Volumetric (3D) transforms","text":"

Transforms designed for three-dimensional data (D, H, W). These operate on volumes and their corresponding 3D masks, supporting both single-channel and multi-channel data.

Transform Image Mask BBoxes Keypoints Volume Mask3D CenterCrop3D \u2713 \u2713 \u2713 CoarseDropout3D \u2713 \u2713 \u2713 CubicSymmetry \u2713 \u2713 \u2713 Pad3D \u2713 \u2713 \u2713 PadIfNeeded3D \u2713 \u2713 \u2713 RandomCrop3D \u2713 \u2713 \u2713"},{"location":"api_reference/augmentations/","title":"Index","text":"
  • Transforms (albumentations.augmentations.transforms)
  • Blur transforms (albumentations.augmentations.blur)
  • Crop transforms (albumentations.augmentations.crops)
  • Dropout transforms (albumentations.augmentations.dropout)
  • Geometric transforms (albumentations.augmentations.geometric)
  • Domain adaptation transforms (albumentations.augmentations.domain_adaptation)
  • Functional transforms (albumentations.augmentations.functional)
"},{"location":"api_reference/augmentations/domain_adaptation/","title":"Domain adaptation transforms (augmentations.domain_adaptation)","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional","title":"functional","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.apply_histogram","title":"def apply_histogram (img, reference_image, blend_ratio) [view source on GitHub]","text":"

Apply histogram matching to an input image using a reference image and blend the result.

This function performs histogram matching between the input image and a reference image, then blends the result with the original input image based on the specified blend ratio.

Parameters:

Name Type Description img np.ndarray

The input image to be transformed. Can be either grayscale or RGB. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

reference_image np.ndarray

The reference image used for histogram matching. Should have the same number of channels as the input image. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

blend_ratio float

The ratio for blending the matched image with the original image. Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.

Returns:

Type Description np.ndarray

The transformed image after histogram matching and blending. The output will have the same shape and dtype as the input image.

Supported image types: - Grayscale images: 2D arrays - RGB images: 3D arrays with 3 channels - Multispectral images: 3D arrays with more than 3 channels

Note

  • If the input and reference images have different sizes, the reference image will be resized to match the input image's dimensions.
  • The function uses a custom implementation of histogram matching based on OpenCV and NumPy.
  • The @clipped and @preserve_channel_dim decorators ensure the output is within the valid range and maintains the original number of dimensions.
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef apply_histogram(img: np.ndarray, reference_image: np.ndarray, blend_ratio: float) -> np.ndarray:\n    \"\"\"Apply histogram matching to an input image using a reference image and blend the result.\n\n    This function performs histogram matching between the input image and a reference image,\n    then blends the result with the original input image based on the specified blend ratio.\n\n    Args:\n        img (np.ndarray): The input image to be transformed. Can be either grayscale or RGB.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        reference_image (np.ndarray): The reference image used for histogram matching.\n            Should have the same number of channels as the input image.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        blend_ratio (float): The ratio for blending the matched image with the original image.\n            Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.\n\n    Returns:\n        np.ndarray: The transformed image after histogram matching and blending.\n            The output will have the same shape and dtype as the input image.\n\n    Supported image types:\n        - Grayscale images: 2D arrays\n        - RGB images: 3D arrays with 3 channels\n        - Multispectral images: 3D arrays with more than 3 channels\n\n    Note:\n        - If the input and reference images have different sizes, the reference image\n          will be resized to match the input image's dimensions.\n        - The function uses a custom implementation of histogram matching based on OpenCV and NumPy.\n        - The @clipped and @preserve_channel_dim decorators ensure the output is within\n          the valid range and maintains the original number of dimensions.\n    \"\"\"\n    # Resize reference image only if necessary\n    if img.shape[:2] != reference_image.shape[:2]:\n        reference_image = cv2.resize(reference_image, dsize=(img.shape[1], img.shape[0]))\n\n    img = np.squeeze(img)\n    reference_image = np.squeeze(reference_image)\n\n    # Match histograms between the images\n    matched = match_histograms(img, reference_image)\n\n    # Blend the original image and the matched image\n    return add_weighted(matched, blend_ratio, img, 1 - blend_ratio)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.fourier_domain_adaptation","title":"def fourier_domain_adaptation (img, target_img, beta) [view source on GitHub]","text":"

Apply Fourier Domain Adaptation to the input image using a target image.

This function performs domain adaptation in the frequency domain by modifying the amplitude spectrum of the source image based on the target image's amplitude spectrum. It preserves the phase information of the source image, which helps maintain its content while adapting its style to match the target image.

Parameters:

Name Type Description img np.ndarray

The source image to be adapted. Can be grayscale or RGB.

target_img np.ndarray

The target image used as a reference for adaptation. Should have the same dimensions as the source image.

beta float

The adaptation strength, typically in the range [0, 1]. Higher values result in stronger adaptation towards the target image's style.

Returns:

Type Description np.ndarray

The adapted image with the same shape and type as the input image.

Exceptions:

Type Description ValueError

If the source and target images have different shapes.

Note

  • Both input images are converted to float32 for processing.
  • The function handles both grayscale (2D) and color (3D) images.
  • For grayscale images, an extra dimension is added to facilitate uniform processing.
  • The adaptation is performed channel-wise for color images.
  • The output is clipped to the valid range and preserves the original number of channels.

The adaptation process involves the following steps for each channel: 1. Compute the 2D Fourier Transform of both source and target images. 2. Shift the zero frequency component to the center of the spectrum. 3. Extract amplitude and phase information from the source image's spectrum. 4. Mutate the source amplitude using the target amplitude and the beta parameter. 5. Combine the mutated amplitude with the original phase. 6. Perform the inverse Fourier Transform to obtain the adapted channel.

The low_freq_mutate function (not shown here) is responsible for the actual amplitude mutation, focusing on low-frequency components which carry style information.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n>>> assert adapted_img.shape == source_img.shape\n

References

  • \"FDA: Fourier Domain Adaptation for Semantic Segmentation\" (Yang and Soatto, 2020, CVPR) https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef fourier_domain_adaptation(img: np.ndarray, target_img: np.ndarray, beta: float) -> np.ndarray:\n    \"\"\"Apply Fourier Domain Adaptation to the input image using a target image.\n\n    This function performs domain adaptation in the frequency domain by modifying the amplitude\n    spectrum of the source image based on the target image's amplitude spectrum. It preserves\n    the phase information of the source image, which helps maintain its content while adapting\n    its style to match the target image.\n\n    Args:\n        img (np.ndarray): The source image to be adapted. Can be grayscale or RGB.\n        target_img (np.ndarray): The target image used as a reference for adaptation.\n            Should have the same dimensions as the source image.\n        beta (float): The adaptation strength, typically in the range [0, 1].\n            Higher values result in stronger adaptation towards the target image's style.\n\n    Returns:\n        np.ndarray: The adapted image with the same shape and type as the input image.\n\n    Raises:\n        ValueError: If the source and target images have different shapes.\n\n    Note:\n        - Both input images are converted to float32 for processing.\n        - The function handles both grayscale (2D) and color (3D) images.\n        - For grayscale images, an extra dimension is added to facilitate uniform processing.\n        - The adaptation is performed channel-wise for color images.\n        - The output is clipped to the valid range and preserves the original number of channels.\n\n    The adaptation process involves the following steps for each channel:\n    1. Compute the 2D Fourier Transform of both source and target images.\n    2. Shift the zero frequency component to the center of the spectrum.\n    3. Extract amplitude and phase information from the source image's spectrum.\n    4. Mutate the source amplitude using the target amplitude and the beta parameter.\n    5. Combine the mutated amplitude with the original phase.\n    6. Perform the inverse Fourier Transform to obtain the adapted channel.\n\n    The `low_freq_mutate` function (not shown here) is responsible for the actual\n    amplitude mutation, focusing on low-frequency components which carry style information.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n        >>> assert adapted_img.shape == source_img.shape\n\n    References:\n        - \"FDA: Fourier Domain Adaptation for Semantic Segmentation\"\n          (Yang and Soatto, 2020, CVPR)\n          https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n    \"\"\"\n    src_img = img.astype(np.float32)\n    trg_img = target_img.astype(np.float32)\n\n    if src_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        src_img = np.expand_dims(src_img, axis=-1)\n    if trg_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        trg_img = np.expand_dims(trg_img, axis=-1)\n\n    num_channels = src_img.shape[-1]\n\n    # Prepare container for the output image\n    src_in_trg = np.zeros_like(src_img)\n\n    for channel_id in range(num_channels):\n        # Perform FFT on each channel\n        fft_src = np.fft.fft2(src_img[:, :, channel_id])\n        fft_trg = np.fft.fft2(trg_img[:, :, channel_id])\n\n        # Shift the zero frequency component to the center\n        fft_src_shifted = np.fft.fftshift(fft_src)\n        fft_trg_shifted = np.fft.fftshift(fft_trg)\n\n        # Extract amplitude and phase\n        amp_src, pha_src = np.abs(fft_src_shifted), np.angle(fft_src_shifted)\n        amp_trg = np.abs(fft_trg_shifted)\n\n        # Mutate the amplitude part of the source with the target\n        mutated_amp = low_freq_mutate(amp_src.copy(), amp_trg, beta)\n\n        # Combine the mutated amplitude with the original phase\n        fft_src_mutated = np.fft.ifftshift(mutated_amp * np.exp(1j * pha_src))\n\n        # Perform inverse FFT\n        src_in_trg_channel = np.fft.ifft2(fft_src_mutated)\n\n        # Store the result in the corresponding channel of the output image\n        src_in_trg[:, :, channel_id] = np.real(src_in_trg_channel)\n\n    return src_in_trg\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.functional.match_histograms","title":"def match_histograms (image, reference) [view source on GitHub]","text":"

Adjust an image so that its cumulative histogram matches that of another.

The adjustment is applied separately for each channel.

Parameters:

Name Type Description image np.ndarray

Input image. Can be gray-scale or in color.

reference np.ndarray

Image to match histogram of. Must have the same number of channels as image.

channel_axis

If None, the image is assumed to be a grayscale (single channel) image. Otherwise, this parameter indicates which axis of the array corresponds to channels.

Returns:

Type Description np.ndarray

Transformed input image.

Exceptions:

Type Description ValueError

Thrown when the number of channels in the input image and the reference differ.

Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef match_histograms(image: np.ndarray, reference: np.ndarray) -> np.ndarray:\n    \"\"\"Adjust an image so that its cumulative histogram matches that of another.\n\n    The adjustment is applied separately for each channel.\n\n    Args:\n        image: Input image. Can be gray-scale or in color.\n        reference: Image to match histogram of. Must have the same number of channels as image.\n        channel_axis: If None, the image is assumed to be a grayscale (single channel) image.\n            Otherwise, this parameter indicates which axis of the array corresponds to channels.\n\n    Returns:\n        np.ndarray: Transformed input image.\n\n    Raises:\n        ValueError: Thrown when the number of channels in the input image and the reference differ.\n    \"\"\"\n    if reference.dtype != np.uint8:\n        reference = from_float(reference, np.uint8)\n\n    if image.ndim != reference.ndim:\n        raise ValueError(\"Image and reference must have the same number of dimensions.\")\n\n    # Expand dimensions for grayscale images\n    if image.ndim == 2:\n        image = np.expand_dims(image, axis=-1)\n    if reference.ndim == 2:\n        reference = np.expand_dims(reference, axis=-1)\n\n    matched = np.empty(image.shape, dtype=np.uint8)\n\n    num_channels = image.shape[-1]\n\n    for channel in range(num_channels):\n        matched_channel = _match_cumulative_cdf(image[..., channel], reference[..., channel]).astype(np.uint8)\n        matched[..., channel] = matched_channel\n\n    return matched\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms","title":"transforms","text":""},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.FDA","title":"class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Reference

  • https://github.com/YanchaoYang/FDA
  • https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n>>> result = aug(image=image)\n

Note

FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target domain samples are unavailable. It enables significant improvements in model generalization by aligning the low-level statistics of source and target images through a simple yet effective Fourier-based method.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class FDA(ImageOnlyTransform):\n    \"\"\"Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation\n    (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source\n    and target datasets, effectively adapting images from one domain to closely resemble those from another without\n    altering their semantic content.\n\n    This transform is particularly beneficial in scenarios where the training (source) and testing (target) images\n    come from different distributions, such as synthetic versus real images, or day versus night scenes.\n    Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain\n    alignment by swapping low-frequency components of the Fourier transform between the source and target images.\n    This technique has shown to improve the performance of models on the target domain, particularly for tasks\n    like semantic segmentation, without additional training for domain invariance.\n\n    The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more\n    of the original image's characteristics and higher values leading to more pronounced adaptation effects.\n    It is recommended to use beta values less than 0.3 to avoid introducing artifacts.\n\n    Args:\n        reference_images (Sequence[Any]): Sequence of objects to be converted into images by `read_fn`. This typically\n            involves paths to images that serve as target domain examples for adaptation.\n        beta_limit (tuple[float, float] | float): Coefficient beta from the paper, controlling the swapping extent of\n            frequency components. If one value is provided beta will be sampled from uniform\n            distribution [0, beta_limit]. Values should be less than 0.5.\n        read_fn (Callable): User-defined function for reading images. It takes an element from `reference_images` and\n            returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a\n            numpy array.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        - https://github.com/YanchaoYang/FDA\n        - https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n        >>> result = aug(image=image)\n\n    Note:\n        FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target\n        domain samples are unavailable. It enables significant improvements in model generalization by aligning\n        the low-level statistics of source and target images through a simple yet effective Fourier-based method.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        read_fn: Callable[[Any], np.ndarray]\n        beta_limit: ZeroOneRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_ranges(cls, value: tuple[float, float]) -> tuple[float, float]:\n            bounds = 0, MAX_BETA_LIMIT\n            if not bounds[0] <= value[0] <= value[1] <= bounds[1]:\n                raise ValueError(f\"Values should be in the range {bounds} got {value} \")\n            return value\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        beta_limit: ScaleFloatType = (0, 0.1),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        target_image: np.ndarray,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fourier_domain_adaptation(img, target_image, beta)\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n        target_img = self.read_fn(self.py_random.choice(self.reference_images))\n        target_img = cv2.resize(target_img, dsize=(width, height))\n\n        return {\"target_image\": target_img, \"beta\": self.py_random.uniform(*self.beta_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"reference_images\", \"beta_limit\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"FDA can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.HistogramMatching","title":"class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.HistogramMatching(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     read_fn=lambda x: x,\n...     p=1\n... )\n>>> result = transform(image=image)\n>>> matched_image = result[\"image\"]\n

References

  • Histogram Matching in scikit-image: https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class HistogramMatching(ImageOnlyTransform):\n    \"\"\"Adjust the pixel values of an input image to match the histogram of a reference image.\n\n    This transform applies histogram matching, a technique that modifies the distribution of pixel\n    intensities in the input image to closely resemble that of a reference image. This process is\n    performed independently for each channel in multi-channel images, provided both the input and\n    reference images have the same number of channels.\n\n    Histogram matching is particularly useful for:\n    - Normalizing images from different sources or captured under varying conditions.\n    - Preparing images for feature matching or other computer vision tasks where consistent\n      tone and contrast are important.\n    - Simulating different lighting or camera conditions in a controlled manner.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of reference image sources. These can be\n            file paths, URLs, or any objects that can be converted to images by the `read_fn`.\n        blend_ratio (tuple[float, float]): Range for the blending factor between the original\n            and the matched image. Must be two floats between 0 and 1, where:\n            - 0 means no blending (original image is returned)\n            - 1 means full histogram matching\n            A random value within this range is chosen for each application.\n            Default: (0.5, 1.0)\n        read_fn (Callable[[Any], np.ndarray]): A function that takes an element from\n            `reference_images` and returns a numpy array representing the image.\n            Default: read_rgb_image (reads image file from disk)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform cannot be directly serialized due to its dependency on external image data.\n        - The effectiveness of the matching depends on the similarity between the input and reference images.\n        - For best results, choose reference images that represent the desired tone and contrast.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.HistogramMatching(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     read_fn=lambda x: x,\n        ...     p=1\n        ... )\n        >>> result = transform(image=image)\n        >>> matched_image = result[\"image\"]\n\n    References:\n        - Histogram Matching in scikit-image:\n          https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.5, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n\n    def apply(\n        self: np.ndarray,\n        img: np.ndarray,\n        reference_image: np.ndarray,\n        blend_ratio: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return apply_histogram(img, reference_image, blend_ratio)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"HistogramMatching can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.PixelDistributionAdaptation","title":"class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub]","text":"

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal[\"pca\", \"standard\", \"minmax\"]

Specifies the type of statistical transformation to apply. - \"pca\": Principal Component Analysis - \"standard\": StandardScaler (zero mean and unit variance) - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: \"pca\"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PixelDistributionAdaptation(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     transform_type=\"standard\",\n...     read_fn=lambda x: x,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> adapted_image = result[\"image\"]\n

References

  • https://github.com/arsenyinfo/qudida
  • https://arxiv.org/abs/1911.11483

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class PixelDistributionAdaptation(ImageOnlyTransform):\n    \"\"\"Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image\n    with that of a reference image. This process involves fitting a simple statistical transformation\n    (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images,\n    transforming the original image with the transformation trained on it, and then applying the inverse\n    transformation using the transform fitted on the reference image. The result is an adapted image\n    that retains the original content while mimicking the pixel value distribution of the reference domain.\n\n    The process can be visualized as two main steps:\n    1. Adjusting the original image to a standard distribution space using a selected transform.\n    2. Moving the adjusted image into the distribution space of the reference image by applying the inverse\n       of the transform fitted on the reference image.\n\n    This technique is especially useful in scenarios where images from different domains (e.g., synthetic\n    vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in\n    image processing tasks.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of objects (typically image paths) that will be\n            converted into images by `read_fn`. These images serve as references for the domain adaptation.\n        blend_ratio (tuple[float, float]): Specifies the minimum and maximum blend ratio for mixing\n            the adapted image with the original. This enhances the diversity of the output images.\n            Values should be in the range [0, 1]. Default: (0.25, 1.0)\n        read_fn (Callable): A user-defined function for reading and converting the objects in\n            `reference_images` into numpy arrays. By default, it assumes these objects are image paths.\n        transform_type (Literal[\"pca\", \"standard\", \"minmax\"]): Specifies the type of statistical\n            transformation to apply.\n            - \"pca\": Principal Component Analysis\n            - \"standard\": StandardScaler (zero mean and unit variance)\n            - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1])\n            Default: \"pca\"\n        p (float): The probability of applying the transform to any given image. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effectiveness of the adaptation depends on the similarity between the input and reference domains.\n        - PCA transformation may alter color relationships more significantly than other methods.\n        - StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.\n        - The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.\n        - This transform cannot be directly serialized due to its dependency on external image data.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PixelDistributionAdaptation(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     transform_type=\"standard\",\n        ...     read_fn=lambda x: x,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> adapted_image = result[\"image\"]\n\n    References:\n        - https://github.com/arsenyinfo/qudida\n        - https://arxiv.org/abs/1911.11483\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.25, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"] = \"pca\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n        self.transform_type = transform_type\n\n    def apply(self, img: np.ndarray, reference_image: np.ndarray, blend_ratio: float, **params: Any) -> np.ndarray:\n        return adapt_pixel_distribution(\n            img,\n            ref=reference_image,\n            weight=blend_ratio,\n            transform_type=self.transform_type,\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\", \"transform_type\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"PixelDistributionAdaptation can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms.TemplateTransform","title":"class TemplateTransform (templates, img_weight=(0.5, 0.5), template_weight=None, template_transform=None, name=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply blending of input image with specified templates.

This transform overlays one or more template images onto the input image using alpha blending. It allows for creating complex composite images or simulating various visual effects.

Parameters:

Name Type Description templates numpy array | list[np.ndarray]

Images to use as templates for the transform. If a single numpy array is provided, it will be used as the only template. If a list of numpy arrays is provided, one will be randomly chosen for each application.

img_weight tuple[float, float] | float

Weight of the original image in the blend. If a single float, that value will always be used. If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application. To use a fixed weight, use (weight, weight). Default: (0.5, 0.5).

template_transform A.Compose | None

A composition of Albumentations transforms to apply to the template before blending. This should be an instance of A.Compose containing one or more Albumentations transforms. Default: None.

name str | None

Name of the transform instance. Used for serialization purposes. Default: None.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The template(s) must have the same number of channels as the input image or be single-channel.
  • If a single-channel template is used with a multi-channel image, the template will be replicated across all channels.
  • The template(s) will be resized to match the input image size if they differ.
  • To make this transform serializable, provide a name when initializing it.

Mathematical Formulation: Given: - I: Input image - T: Template image - w_i: Weight of input image (sampled from img_weight)

The blended image B is computed as:\n\nB = w_i * I + (1 - w_i) * T\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-a-single-template","title":"Apply template transform with a single template","text":"Python
>>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-multiple-templates-and-custom-weights","title":"Apply template transform with multiple templates and custom weights","text":"Python
>>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n>>> transform = A.TemplateTransform(\n...     templates=templates,\n...     img_weight=(0.3, 0.7),\n...     name=\"multi_template_transform\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-additional-transforms-on-the-template","title":"Apply template transform with additional transforms on the template","text":"Python
>>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n>>> transform = A.TemplateTransform(\n...     templates=template,\n...     img_weight=0.6,\n...     template_transform=template_transform,\n...     name=\"transformed_template\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Image blending: https://en.wikipedia.org/wiki/Image_blending

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class TemplateTransform(ImageOnlyTransform):\n    \"\"\"Apply blending of input image with specified templates.\n\n    This transform overlays one or more template images onto the input image using alpha blending.\n    It allows for creating complex composite images or simulating various visual effects.\n\n    Args:\n        templates (numpy array | list[np.ndarray]): Images to use as templates for the transform.\n            If a single numpy array is provided, it will be used as the only template.\n            If a list of numpy arrays is provided, one will be randomly chosen for each application.\n\n        img_weight (tuple[float, float]  | float): Weight of the original image in the blend.\n            If a single float, that value will always be used.\n            If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application.\n            To use a fixed weight, use (weight, weight).\n            Default: (0.5, 0.5).\n\n        template_transform (A.Compose | None): A composition of Albumentations transforms to apply to the template\n            before blending.\n            This should be an instance of A.Compose containing one or more Albumentations transforms.\n            Default: None.\n\n        name (str | None): Name of the transform instance. Used for serialization purposes.\n            Default: None.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The template(s) must have the same number of channels as the input image or be single-channel.\n        - If a single-channel template is used with a multi-channel image, the template will be replicated across\n          all channels.\n        - The template(s) will be resized to match the input image size if they differ.\n        - To make this transform serializable, provide a name when initializing it.\n\n    Mathematical Formulation:\n        Given:\n        - I: Input image\n        - T: Template image\n        - w_i: Weight of input image (sampled from img_weight)\n\n        The blended image B is computed as:\n\n        B = w_i * I + (1 - w_i) * T\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply template transform with a single template\n        >>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with multiple templates and custom weights\n        >>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n        >>> transform = A.TemplateTransform(\n        ...     templates=templates,\n        ...     img_weight=(0.3, 0.7),\n        ...     name=\"multi_template_transform\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with additional transforms on the template\n        >>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n        >>> transform = A.TemplateTransform(\n        ...     templates=template,\n        ...     img_weight=0.6,\n        ...     template_transform=template_transform,\n        ...     name=\"transformed_template\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Image blending: https://en.wikipedia.org/wiki/Image_blending\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        templates: np.ndarray | Sequence[np.ndarray]\n        img_weight: ZeroOneRangeType\n        template_weight: ZeroOneRangeType | None = Field(\n            deprecated=\"Template_weight is deprecated. Computed automatically as (1 - img_weight)\",\n        )\n        template_transform: Compose | BasicTransform | None = None\n        name: str | None\n\n        @field_validator(\"templates\")\n        @classmethod\n        def validate_templates(cls, v: np.ndarray | list[np.ndarray]) -> list[np.ndarray]:\n            if isinstance(v, np.ndarray):\n                return [v]\n            if isinstance(v, list):\n                if not all(isinstance(item, np.ndarray) for item in v):\n                    msg = \"All templates must be numpy arrays.\"\n                    raise ValueError(msg)\n                return v\n            msg = \"Templates must be a numpy array or a list of numpy arrays.\"\n            raise TypeError(msg)\n\n    def __init__(\n        self,\n        templates: np.ndarray | list[np.ndarray],\n        img_weight: ScaleFloatType = (0.5, 0.5),\n        template_weight: None = None,\n        template_transform: Compose | BasicTransform | None = None,\n        name: str | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.templates = templates\n        self.img_weight = cast(tuple[float, float], img_weight)\n        self.template_transform = template_transform\n        self.name = name\n\n    def apply(\n        self,\n        img: np.ndarray,\n        template: np.ndarray,\n        img_weight: float,\n        **params: Any,\n    ) -> np.ndarray:\n        if img_weight == 0:\n            return template\n        if img_weight == 1:\n            return img\n\n        return add_weighted(img, img_weight, template, 1 - img_weight)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"img_weight\": self.py_random.uniform(*self.img_weight),\n        }\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        template = self.py_random.choice(self.templates)\n\n        if self.template_transform is not None:\n            template = self.template_transform(image=template)[\"image\"]\n\n        if get_num_channels(template) not in [1, get_num_channels(image)]:\n            msg = (\n                \"Template must be a single channel or \"\n                \"has the same number of channels as input \"\n                f\"image ({get_num_channels(image)}), got {get_num_channels(template)}\"\n            )\n            raise ValueError(msg)\n\n        if template.dtype != image.dtype:\n            msg = \"Image and template must be the same image type\"\n            raise ValueError(msg)\n\n        if image.shape[:2] != template.shape[:2]:\n            template = fgeometric.resize(template, image.shape[:2], interpolation=cv2.INTER_AREA)\n\n        if get_num_channels(template) == 1 and get_num_channels(image) > 1:\n            # Replicate single channel template across all channels to match input image\n            template = cv2.merge([template] * get_num_channels(image))\n        # in order to support grayscale image with dummy dim\n        template = template.reshape(image.shape)\n\n        return {\"template\": template}\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a TemplateTransform serializable you should provide the `name` argument, \"\n                \"e.g. `TemplateTransform(name='my_transform', ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n
"},{"location":"api_reference/augmentations/functional/","title":"Functional transforms (augmentations.functional)","text":""},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_fog","title":"def add_fog (img, fog_intensity, alpha_coef, fog_particle_positions, fog_particle_radiuses) [view source on GitHub]","text":"

Add fog to the input image.

Parameters:

Name Type Description img np.ndarray

Input image.

fog_intensity float

Intensity of the fog effect, between 0 and 1.

alpha_coef float

Base alpha (transparency) value for fog particles.

fog_particle_positions list[tuple[int, int]]

List of (x, y) coordinates for fog particles.

fog_particle_radiuses list[int]

List of radiuses for each fog particle.

Returns:

Type Description np.ndarray

Image with added fog effect.

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\n@preserve_channel_dim\ndef add_fog(\n    img: np.ndarray,\n    fog_intensity: float,\n    alpha_coef: float,\n    fog_particle_positions: list[tuple[int, int]],\n    fog_particle_radiuses: list[int],\n) -> np.ndarray:\n    \"\"\"Add fog to the input image.\n\n    Args:\n        img (np.ndarray): Input image.\n        fog_intensity (float): Intensity of the fog effect, between 0 and 1.\n        alpha_coef (float): Base alpha (transparency) value for fog particles.\n        fog_particle_positions (list[tuple[int, int]]): List of (x, y) coordinates for fog particles.\n        fog_particle_radiuses (list[int]): List of radiuses for each fog particle.\n\n    Returns:\n        np.ndarray: Image with added fog effect.\n    \"\"\"\n    height, width = img.shape[:2]\n    num_channels = get_num_channels(img)\n\n    fog_layer = np.zeros((height, width, num_channels), dtype=np.uint8)\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    for (x, y), radius in zip(fog_particle_positions, fog_particle_radiuses):\n        color = max_value if num_channels == 1 else (max_value,) * num_channels\n        cv2.circle(\n            fog_layer,\n            center=(x, y),\n            radius=radius,\n            color=color,\n            thickness=-1,\n        )\n\n    # Apply gaussian blur to the fog layer\n    fog_layer = cv2.GaussianBlur(fog_layer, (25, 25), 0)\n\n    # Blend the fog layer with the original image\n    alpha = np.mean(fog_layer, axis=2, keepdims=True) / max_value * alpha_coef * fog_intensity\n\n    result = img * (1 - alpha) + fog_layer * alpha\n\n    return clip(result, np.uint8, inplace=True)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_rain","title":"def add_rain (img, slant, drop_length, drop_width, drop_color, blur_value, brightness_coefficient, rain_drops) [view source on GitHub]","text":"

Adds rain drops to the image.

Parameters:

Name Type Description img np.ndarray

Input image.

slant int

The angle of the rain drops.

drop_length int

The length of each rain drop.

drop_width int

The width of each rain drop.

drop_color tuple[int, int, int]

The color of the rain drops in RGB format.

blur_value int

The size of the kernel used to blur the image. Rainy views are blurry.

brightness_coefficient float

Coefficient to adjust the brightness of the image. Rainy days are usually shady.

rain_drops list[tuple[int, int]]

A list of tuples where each tuple represents the (x, y) coordinates of the starting point of a rain drop.

Returns:

Type Description np.ndarray

Image with rain effect added.

Reference

https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef add_rain(\n    img: np.ndarray,\n    slant: int,\n    drop_length: int,\n    drop_width: int,\n    drop_color: tuple[int, int, int],\n    blur_value: int,\n    brightness_coefficient: float,\n    rain_drops: list[tuple[int, int]],\n) -> np.ndarray:\n    \"\"\"Adds rain drops to the image.\n\n    Args:\n        img (np.ndarray): Input image.\n        slant (int): The angle of the rain drops.\n        drop_length (int): The length of each rain drop.\n        drop_width (int): The width of each rain drop.\n        drop_color (tuple[int, int, int]): The color of the rain drops in RGB format.\n        blur_value (int): The size of the kernel used to blur the image. Rainy views are blurry.\n        brightness_coefficient (float): Coefficient to adjust the brightness of the image. Rainy days are usually shady.\n        rain_drops (list[tuple[int, int]]): A list of tuples where each tuple represents the (x, y)\n            coordinates of the starting point of a rain drop.\n\n    Returns:\n        np.ndarray: Image with rain effect added.\n\n    Reference:\n        https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    img = img.copy()\n    for rain_drop_x0, rain_drop_y0 in rain_drops:\n        rain_drop_x1 = rain_drop_x0 + slant\n        rain_drop_y1 = rain_drop_y0 + drop_length\n\n        cv2.line(\n            img,\n            (rain_drop_x0, rain_drop_y0),\n            (rain_drop_x1, rain_drop_y1),\n            drop_color,\n            drop_width,\n        )\n\n    img = cv2.blur(img, (blur_value, blur_value))  # rainy view are blurry\n    image_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)\n    image_hsv[:, :, 2] *= brightness_coefficient\n\n    return cv2.cvtColor(image_hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_shadow","title":"def add_shadow (img, vertices_list, intensities) [view source on GitHub]","text":"

Add shadows to the image by reducing the intensity of the pixel values in specified regions.

Parameters:

Name Type Description img np.ndarray

Input image. Multichannel images are supported.

vertices_list list[np.ndarray]

List of vertices for shadow polygons.

intensities np.ndarray

Array of shadow intensities. Range is [0, 1].

Returns:

Type Description np.ndarray

Image with shadows added.

Reference

https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef add_shadow(\n    img: np.ndarray,\n    vertices_list: list[np.ndarray],\n    intensities: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Add shadows to the image by reducing the intensity of the pixel values in specified regions.\n\n    Args:\n        img (np.ndarray): Input image. Multichannel images are supported.\n        vertices_list (list[np.ndarray]): List of vertices for shadow polygons.\n        intensities (np.ndarray): Array of shadow intensities. Range is [0, 1].\n\n    Returns:\n        np.ndarray: Image with shadows added.\n\n    Reference:\n        https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    num_channels = get_num_channels(img)\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    img_shadowed = img.copy()\n\n    # Iterate over the vertices and intensity list\n    for vertices, shadow_intensity in zip(vertices_list, intensities):\n        # Create mask for the current shadow polygon\n        mask = np.zeros((img.shape[0], img.shape[1], 1), dtype=np.uint8)\n        cv2.fillPoly(mask, [vertices], (max_value,))\n\n        # Duplicate the mask to have the same number of channels as the image\n        mask = np.repeat(mask, num_channels, axis=2)\n\n        # Apply shadow to the channels directly\n        # It could be tempting to convert to HLS and apply the shadow to the L channel, but it creates artifacts\n        shadowed_indices = mask[:, :, 0] == max_value\n        darkness = 1 - shadow_intensity\n        img_shadowed[shadowed_indices] = clip(\n            img_shadowed[shadowed_indices] * darkness,\n            np.uint8,\n            inplace=True,\n        )\n\n    return img_shadowed\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_snow_bleach","title":"def add_snow_bleach (img, snow_point, brightness_coeff) [view source on GitHub]","text":"

Adds a simple snow effect to the image by bleaching out pixels.

This function simulates a basic snow effect by increasing the brightness of pixels that are above a certain threshold (snow_point). It operates in the HLS color space to modify the lightness channel.

Parameters:

Name Type Description img np.ndarray

Input image. Can be either RGB uint8 or float32.

snow_point float

A float in the range [0, 1], scaled and adjusted to determine the threshold for pixel modification. Higher values result in less snow effect.

brightness_coeff float

Coefficient applied to increase the brightness of pixels below the snow_point threshold. Larger values lead to more pronounced snow effects. Should be greater than 1.0 for a visible effect.

Returns:

Type Description np.ndarray

Image with simulated snow effect. The output has the same dtype as the input.

Note

  • This function converts the image to the HLS color space to modify the lightness channel.
  • The snow effect is created by selectively increasing the brightness of pixels.
  • This method tends to create a 'bleached' look, which may not be as realistic as more advanced snow simulation techniques.
  • The function automatically handles both uint8 and float32 input images.

The snow effect is created through the following steps: 1. Convert the image from RGB to HLS color space. 2. Adjust the snow_point threshold. 3. Increase the lightness of pixels below the threshold. 4. Convert the image back to RGB.

Mathematical Formulation: Let L be the lightness channel in HLS space. For each pixel (i, j): If L[i, j] < snow_point: L[i, j] = L[i, j] * brightness_coeff

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> snowy_image = A.functional.add_snow_v1(image, snow_point=0.5, brightness_coeff=1.5)\n

References

  • HLS Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV
  • Original implementation: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library
Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef add_snow_bleach(\n    img: np.ndarray,\n    snow_point: float,\n    brightness_coeff: float,\n) -> np.ndarray:\n    \"\"\"Adds a simple snow effect to the image by bleaching out pixels.\n\n    This function simulates a basic snow effect by increasing the brightness of pixels\n    that are above a certain threshold (snow_point). It operates in the HLS color space\n    to modify the lightness channel.\n\n    Args:\n        img (np.ndarray): Input image. Can be either RGB uint8 or float32.\n        snow_point (float): A float in the range [0, 1], scaled and adjusted to determine\n            the threshold for pixel modification. Higher values result in less snow effect.\n        brightness_coeff (float): Coefficient applied to increase the brightness of pixels\n            below the snow_point threshold. Larger values lead to more pronounced snow effects.\n            Should be greater than 1.0 for a visible effect.\n\n    Returns:\n        np.ndarray: Image with simulated snow effect. The output has the same dtype as the input.\n\n    Note:\n        - This function converts the image to the HLS color space to modify the lightness channel.\n        - The snow effect is created by selectively increasing the brightness of pixels.\n        - This method tends to create a 'bleached' look, which may not be as realistic as more\n          advanced snow simulation techniques.\n        - The function automatically handles both uint8 and float32 input images.\n\n    The snow effect is created through the following steps:\n    1. Convert the image from RGB to HLS color space.\n    2. Adjust the snow_point threshold.\n    3. Increase the lightness of pixels below the threshold.\n    4. Convert the image back to RGB.\n\n    Mathematical Formulation:\n        Let L be the lightness channel in HLS space.\n        For each pixel (i, j):\n        If L[i, j] < snow_point:\n            L[i, j] = L[i, j] * brightness_coeff\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> snowy_image = A.functional.add_snow_v1(image, snow_point=0.5, brightness_coeff=1.5)\n\n    References:\n        - HLS Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV\n        - Original implementation: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    snow_point *= max_value / 2\n    snow_point += max_value / 3\n\n    image_hls = cv2.cvtColor(img, cv2.COLOR_RGB2HLS)\n    image_hls = np.array(image_hls, dtype=np.float32)\n\n    image_hls[:, :, 1][image_hls[:, :, 1] < snow_point] *= brightness_coeff\n\n    image_hls[:, :, 1] = clip(image_hls[:, :, 1], np.uint8, inplace=True)\n\n    image_hls = np.array(image_hls, dtype=np.uint8)\n\n    return cv2.cvtColor(image_hls, cv2.COLOR_HLS2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_snow_texture","title":"def add_snow_texture (img, snow_point, brightness_coeff, snow_texture, sparkle_mask) [view source on GitHub]","text":"

Add a realistic snow effect to the input image.

This function simulates snowfall by applying multiple visual effects to the image, including brightness adjustment, snow texture overlay, depth simulation, and color tinting. The result is a more natural-looking snow effect compared to simple pixel bleaching methods.

Parameters:

Name Type Description img np.ndarray

Input image in RGB format.

snow_point float

Coefficient that controls the amount and intensity of snow. Should be in the range [0, 1], where 0 means no snow and 1 means maximum snow effect.

brightness_coeff float

Coefficient for brightness adjustment to simulate the reflective nature of snow. Should be in the range [0, 1], where higher values result in a brighter image.

snow_texture np.ndarray

Snow texture.

sparkle_mask np.ndarray

Sparkle mask.

Returns:

Type Description np.ndarray

Image with added snow effect. The output has the same dtype as the input.

Note

  • The function first converts the image to HSV color space for better control over brightness and color adjustments.
  • A snow texture is generated using Gaussian noise and then filtered for a more natural appearance.
  • A depth effect is simulated, with more snow at the top of the image and less at the bottom.
  • A slight blue tint is added to simulate the cool color of snow.
  • Random sparkle effects are added to simulate light reflecting off snow crystals.

The snow effect is created through the following steps: 1. Brightness adjustment in HSV space 2. Generation of a snow texture using Gaussian noise 3. Application of a depth effect to the snow texture 4. Blending of the snow texture with the original image 5. Addition of a cool blue tint 6. Addition of sparkle effects

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> snowy_image = A.functional.add_snow_v2(image, snow_coeff=0.5, brightness_coeff=0.2)\n

Note

This function works with both uint8 and float32 image types, automatically handling the conversion between them.

References

  • Perlin Noise: https://en.wikipedia.org/wiki/Perlin_noise
  • HSV Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV
Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef add_snow_texture(\n    img: np.ndarray,\n    snow_point: float,\n    brightness_coeff: float,\n    snow_texture: np.ndarray,\n    sparkle_mask: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Add a realistic snow effect to the input image.\n\n    This function simulates snowfall by applying multiple visual effects to the image,\n    including brightness adjustment, snow texture overlay, depth simulation, and color tinting.\n    The result is a more natural-looking snow effect compared to simple pixel bleaching methods.\n\n    Args:\n        img (np.ndarray): Input image in RGB format.\n        snow_point (float): Coefficient that controls the amount and intensity of snow.\n            Should be in the range [0, 1], where 0 means no snow and 1 means maximum snow effect.\n        brightness_coeff (float): Coefficient for brightness adjustment to simulate the\n            reflective nature of snow. Should be in the range [0, 1], where higher values\n            result in a brighter image.\n        snow_texture (np.ndarray): Snow texture.\n        sparkle_mask (np.ndarray): Sparkle mask.\n\n    Returns:\n        np.ndarray: Image with added snow effect. The output has the same dtype as the input.\n\n    Note:\n        - The function first converts the image to HSV color space for better control over\n          brightness and color adjustments.\n        - A snow texture is generated using Gaussian noise and then filtered for a more\n          natural appearance.\n        - A depth effect is simulated, with more snow at the top of the image and less at the bottom.\n        - A slight blue tint is added to simulate the cool color of snow.\n        - Random sparkle effects are added to simulate light reflecting off snow crystals.\n\n    The snow effect is created through the following steps:\n    1. Brightness adjustment in HSV space\n    2. Generation of a snow texture using Gaussian noise\n    3. Application of a depth effect to the snow texture\n    4. Blending of the snow texture with the original image\n    5. Addition of a cool blue tint\n    6. Addition of sparkle effects\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> snowy_image = A.functional.add_snow_v2(image, snow_coeff=0.5, brightness_coeff=0.2)\n\n    Note:\n        This function works with both uint8 and float32 image types, automatically\n        handling the conversion between them.\n\n    References:\n        - Perlin Noise: https://en.wikipedia.org/wiki/Perlin_noise\n        - HSV Color Space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[np.uint8]\n\n    # Convert to HSV for better color control\n    img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.float32)\n\n    # Increase brightness\n    img_hsv[:, :, 2] = np.clip(\n        img_hsv[:, :, 2] * (1 + brightness_coeff * snow_point),\n        0,\n        max_value,\n    )\n\n    # Generate snow texture\n    snow_texture = cv2.GaussianBlur(snow_texture, (0, 0), sigmaX=1, sigmaY=1)\n\n    # Create depth effect for snow simulation\n    # More snow accumulates at the top of the image, gradually decreasing towards the bottom\n    # This simulates natural snow distribution on surfaces\n    # The effect is achieved using a linear gradient from 1 (full snow) to 0.2 (less snow)\n    rows = img.shape[0]\n    depth_effect = np.linspace(1, 0.2, rows)[:, np.newaxis]\n    snow_texture *= depth_effect\n\n    # Apply snow texture\n    snow_layer = (np.dstack([snow_texture] * 3) * max_value * snow_point).astype(\n        np.float32,\n    )\n\n    # Blend snow with original image\n    img_with_snow = cv2.add(img_hsv, snow_layer)\n\n    # Add a slight blue tint to simulate cool snow color\n    blue_tint = np.full_like(img_with_snow, (0.6, 0.75, 1))  # Slight blue in HSV\n\n    img_with_snow = cv2.addWeighted(\n        img_with_snow,\n        0.85,\n        blue_tint,\n        0.15 * snow_point,\n        0,\n    )\n\n    # Convert back to RGB\n    img_with_snow = cv2.cvtColor(img_with_snow.astype(np.uint8), cv2.COLOR_HSV2RGB)\n\n    # Add some sparkle effects for snow glitter\n    img_with_snow[sparkle_mask] = [max_value, max_value, max_value]\n\n    return img_with_snow\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_sun_flare_overlay","title":"def add_sun_flare_overlay (img, flare_center, src_radius, src_color, circles) [view source on GitHub]","text":"

Add a sun flare effect to an image using a simple overlay technique.

This function creates a basic sun flare effect by overlaying multiple semi-transparent circles of varying sizes and intensities on the input image. The effect simulates a simple lens flare caused by bright light sources.

Parameters:

Name Type Description img np.ndarray

The input image.

flare_center tuple[float, float]

(x, y) coordinates of the flare center in pixel coordinates.

src_radius int

The radius of the main sun circle in pixels.

src_color tuple[int, ...]

The color of the sun, represented as a tuple of RGB values.

circles list[Any]

A list of tuples, each representing a circle that contributes to the flare effect. Each tuple contains: - alpha (float): The transparency of the circle (0.0 to 1.0). - center (tuple[int, int]): (x, y) coordinates of the circle center. - radius (int): The radius of the circle. - color (tuple[int, int, int]): RGB color of the circle.

Returns:

Type Description np.ndarray

The output image with the sun flare effect added.

Note

  • This function uses a simple alpha blending technique to overlay flare elements.
  • The main sun is created as a gradient circle, fading from the center outwards.
  • Additional flare circles are added along an imaginary line from the sun's position.
  • This method is computationally efficient but may produce less realistic results compared to more advanced techniques.

The flare effect is created through the following steps: 1. Create an overlay image and output image as copies of the input. 2. Add smaller flare circles to the overlay. 3. Blend the overlay with the output image using alpha compositing. 4. Add the main sun circle with a radial gradient.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> flare_center = (50, 50)\n>>> src_radius = 20\n>>> src_color = (255, 255, 200)\n>>> circles = [\n...     (0.1, (60, 60), 5, (255, 200, 200)),\n...     (0.2, (70, 70), 3, (200, 255, 200))\n... ]\n>>> flared_image = A.functional.add_sun_flare_overlay(\n...     image, flare_center, src_radius, src_color, circles\n... )\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\n@maybe_process_in_chunks\ndef add_sun_flare_overlay(\n    img: np.ndarray,\n    flare_center: tuple[float, float],\n    src_radius: int,\n    src_color: tuple[int, ...],\n    circles: list[Any],\n) -> np.ndarray:\n    \"\"\"Add a sun flare effect to an image using a simple overlay technique.\n\n    This function creates a basic sun flare effect by overlaying multiple semi-transparent\n    circles of varying sizes and intensities on the input image. The effect simulates\n    a simple lens flare caused by bright light sources.\n\n    Args:\n        img (np.ndarray): The input image.\n        flare_center (tuple[float, float]): (x, y) coordinates of the flare center\n            in pixel coordinates.\n        src_radius (int): The radius of the main sun circle in pixels.\n        src_color (tuple[int, ...]): The color of the sun, represented as a tuple of RGB values.\n        circles (list[Any]): A list of tuples, each representing a circle that contributes\n            to the flare effect. Each tuple contains:\n            - alpha (float): The transparency of the circle (0.0 to 1.0).\n            - center (tuple[int, int]): (x, y) coordinates of the circle center.\n            - radius (int): The radius of the circle.\n            - color (tuple[int, int, int]): RGB color of the circle.\n\n    Returns:\n        np.ndarray: The output image with the sun flare effect added.\n\n    Note:\n        - This function uses a simple alpha blending technique to overlay flare elements.\n        - The main sun is created as a gradient circle, fading from the center outwards.\n        - Additional flare circles are added along an imaginary line from the sun's position.\n        - This method is computationally efficient but may produce less realistic results\n          compared to more advanced techniques.\n\n    The flare effect is created through the following steps:\n    1. Create an overlay image and output image as copies of the input.\n    2. Add smaller flare circles to the overlay.\n    3. Blend the overlay with the output image using alpha compositing.\n    4. Add the main sun circle with a radial gradient.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> flare_center = (50, 50)\n        >>> src_radius = 20\n        >>> src_color = (255, 255, 200)\n        >>> circles = [\n        ...     (0.1, (60, 60), 5, (255, 200, 200)),\n        ...     (0.2, (70, 70), 3, (200, 255, 200))\n        ... ]\n        >>> flared_image = A.functional.add_sun_flare_overlay(\n        ...     image, flare_center, src_radius, src_color, circles\n        ... )\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n    \"\"\"\n    overlay = img.copy()\n    output = img.copy()\n\n    weighted_brightness = 0.0\n    total_radius_length = 0.0\n\n    for alpha, (x, y), rad3, circle_color in circles:\n        weighted_brightness += alpha * rad3\n        total_radius_length += rad3\n        cv2.circle(overlay, (x, y), rad3, circle_color, -1)\n        output = add_weighted(overlay, alpha, output, 1 - alpha)\n\n    point = [int(x) for x in flare_center]\n\n    overlay = output.copy()\n    num_times = src_radius // 10\n\n    # max_alpha is calculated using weighted_brightness and total_radii_length times 5\n    # meaning the higher the alpha with larger area, the brighter the bright spot will be\n    # for list of alphas in range [0.05, 0.2], the max_alpha should below 1\n    max_alpha = weighted_brightness / total_radius_length * 5\n    alpha = np.linspace(0.0, min(max_alpha, 1.0), num=num_times)\n\n    rad = np.linspace(1, src_radius, num=num_times)\n\n    for i in range(num_times):\n        cv2.circle(overlay, point, int(rad[i]), src_color, -1)\n        alp = alpha[num_times - i - 1] * alpha[num_times - i - 1] * alpha[num_times - i - 1]\n        output = add_weighted(overlay, alp, output, 1 - alp)\n\n    return output\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.add_sun_flare_physics_based","title":"def add_sun_flare_physics_based (img, flare_center, src_radius, src_color, circles) [view source on GitHub]","text":"

Add a more realistic sun flare effect to the image.

This function creates a complex sun flare effect by simulating various optical phenomena that occur in real camera lenses when capturing bright light sources. The result is a more realistic and physically plausible lens flare effect.

Parameters:

Name Type Description img np.ndarray

Input image.

flare_center tuple[int, int]

(x, y) coordinates of the sun's center in pixels.

src_radius int

Radius of the main sun circle in pixels.

src_color tuple[int, int, int]

Color of the sun in RGB format.

circles list[Any]

List of tuples, each representing a flare circle with parameters: (alpha, center, size, color) - alpha (float): Transparency of the circle (0.0 to 1.0). - center (tuple[int, int]): (x, y) coordinates of the circle center. - size (float): Size factor for the circle radius. - color (tuple[int, int, int]): RGB color of the circle.

Returns:

Type Description np.ndarray

Image with added sun flare effect.

Note

This function implements several techniques to create a more realistic flare: 1. Separate flare layer: Allows for complex manipulations of the flare effect. 2. Lens diffraction spikes: Simulates light diffraction in camera aperture. 3. Radial gradient mask: Creates natural fading of the flare from the center. 4. Gaussian blur: Softens the flare for a more natural glow effect. 5. Chromatic aberration: Simulates color fringing often seen in real lens flares. 6. Screen blending: Provides a more realistic blending of the flare with the image.

The flare effect is created through the following steps: 1. Create a separate flare layer. 2. Add the main sun circle and diffraction spikes to the flare layer. 3. Add additional flare circles based on the input parameters. 4. Apply Gaussian blur to soften the flare. 5. Create and apply a radial gradient mask for natural fading. 6. Simulate chromatic aberration by applying different blurs to color channels. 7. Blend the flare with the original image using screen blending mode.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n>>> flare_center = (500, 500)\n>>> src_radius = 50\n>>> src_color = (255, 255, 200)\n>>> circles = [\n...     (0.1, (550, 550), 10, (255, 200, 200)),\n...     (0.2, (600, 600), 5, (200, 255, 200))\n... ]\n>>> flared_image = A.functional.add_sun_flare_physics_based(\n...     image, flare_center, src_radius, src_color, circles\n... )\n

References

  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
  • Diffraction: https://en.wikipedia.org/wiki/Diffraction
  • Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration
  • Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef add_sun_flare_physics_based(\n    img: np.ndarray,\n    flare_center: tuple[int, int],\n    src_radius: int,\n    src_color: tuple[int, int, int],\n    circles: list[Any],\n) -> np.ndarray:\n    \"\"\"Add a more realistic sun flare effect to the image.\n\n    This function creates a complex sun flare effect by simulating various optical phenomena\n    that occur in real camera lenses when capturing bright light sources. The result is a\n    more realistic and physically plausible lens flare effect.\n\n    Args:\n        img (np.ndarray): Input image.\n        flare_center (tuple[int, int]): (x, y) coordinates of the sun's center in pixels.\n        src_radius (int): Radius of the main sun circle in pixels.\n        src_color (tuple[int, int, int]): Color of the sun in RGB format.\n        circles (list[Any]): List of tuples, each representing a flare circle with parameters:\n            (alpha, center, size, color)\n            - alpha (float): Transparency of the circle (0.0 to 1.0).\n            - center (tuple[int, int]): (x, y) coordinates of the circle center.\n            - size (float): Size factor for the circle radius.\n            - color (tuple[int, int, int]): RGB color of the circle.\n\n    Returns:\n        np.ndarray: Image with added sun flare effect.\n\n    Note:\n        This function implements several techniques to create a more realistic flare:\n        1. Separate flare layer: Allows for complex manipulations of the flare effect.\n        2. Lens diffraction spikes: Simulates light diffraction in camera aperture.\n        3. Radial gradient mask: Creates natural fading of the flare from the center.\n        4. Gaussian blur: Softens the flare for a more natural glow effect.\n        5. Chromatic aberration: Simulates color fringing often seen in real lens flares.\n        6. Screen blending: Provides a more realistic blending of the flare with the image.\n\n    The flare effect is created through the following steps:\n    1. Create a separate flare layer.\n    2. Add the main sun circle and diffraction spikes to the flare layer.\n    3. Add additional flare circles based on the input parameters.\n    4. Apply Gaussian blur to soften the flare.\n    5. Create and apply a radial gradient mask for natural fading.\n    6. Simulate chromatic aberration by applying different blurs to color channels.\n    7. Blend the flare with the original image using screen blending mode.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n        >>> flare_center = (500, 500)\n        >>> src_radius = 50\n        >>> src_color = (255, 255, 200)\n        >>> circles = [\n        ...     (0.1, (550, 550), 10, (255, 200, 200)),\n        ...     (0.2, (600, 600), 5, (200, 255, 200))\n        ... ]\n        >>> flared_image = A.functional.add_sun_flare_physics_based(\n        ...     image, flare_center, src_radius, src_color, circles\n        ... )\n\n    References:\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n        - Diffraction: https://en.wikipedia.org/wiki/Diffraction\n        - Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration\n        - Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen\n    \"\"\"\n    output = img.copy()\n    height, width = img.shape[:2]\n\n    # Create a separate flare layer\n    flare_layer = np.zeros_like(img, dtype=np.float32)\n\n    # Add the main sun\n    cv2.circle(flare_layer, flare_center, src_radius, src_color, -1)\n\n    # Add lens diffraction spikes\n    for angle in [0, 45, 90, 135]:\n        end_point = (\n            int(flare_center[0] + np.cos(np.radians(angle)) * max(width, height)),\n            int(flare_center[1] + np.sin(np.radians(angle)) * max(width, height)),\n        )\n        cv2.line(flare_layer, flare_center, end_point, src_color, 2)\n\n    # Add flare circles\n    for _, center, size, color in circles:\n        cv2.circle(flare_layer, center, int(size**0.33), color, -1)\n\n    # Apply gaussian blur to soften the flare\n    flare_layer = cv2.GaussianBlur(flare_layer, (0, 0), sigmaX=15, sigmaY=15)\n\n    # Create a radial gradient mask\n    y, x = np.ogrid[:height, :width]\n    mask = np.sqrt((x - flare_center[0]) ** 2 + (y - flare_center[1]) ** 2)\n    mask = 1 - np.clip(mask / (max(width, height) * 0.7), 0, 1)\n    mask = np.dstack([mask] * 3)\n\n    # Apply the mask to the flare layer\n    flare_layer *= mask\n\n    # Add chromatic aberration\n    channels = list(cv2.split(flare_layer))\n    channels[0] = cv2.GaussianBlur(\n        channels[0],\n        (0, 0),\n        sigmaX=3,\n        sigmaY=3,\n    )  # Blue channel\n    channels[2] = cv2.GaussianBlur(\n        channels[2],\n        (0, 0),\n        sigmaX=5,\n        sigmaY=5,\n    )  # Red channel\n    flare_layer = cv2.merge(channels)\n\n    # Blend the flare with the original image using screen blending\n    return 255 - ((255 - output) * (255 - flare_layer) / 255)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_corner_illumination","title":"def apply_corner_illumination (img, intensity, corner) [view source on GitHub]","text":"

Apply corner-based illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_corner_illumination(\n    img: np.ndarray,\n    intensity: float,\n    corner: Literal[0, 1, 2, 3],\n) -> np.ndarray:\n    \"\"\"Apply corner-based illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create distance map coordinates\n    y, x = np.ogrid[:height, :width]\n\n    # Adjust coordinates based on corner\n    if corner == 1:  # top-right\n        x = width - 1 - x\n    elif corner == 2:  # bottom-right\n        x = width - 1 - x\n        y = height - 1 - y\n    elif corner == 3:  # bottom-left\n        y = height - 1 - y\n\n    # Calculate normalized distance\n    distance = np.sqrt(x * x + y * y) / np.sqrt(height * height + width * width)\n    pattern = 1 - distance  # Invert so corner is brightest\n\n    return apply_illumination_pattern(result, pattern, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_gaussian_illumination","title":"def apply_gaussian_illumination (img, intensity, center, sigma) [view source on GitHub]","text":"

Apply gaussian illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_gaussian_illumination(\n    img: np.ndarray,\n    intensity: float,\n    center: tuple[float, float],\n    sigma: float,\n) -> np.ndarray:\n    \"\"\"Apply gaussian illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create coordinate grid\n    y, x = np.ogrid[:height, :width]\n\n    # Calculate gaussian pattern\n    center_x = width * center[0]\n    center_y = height * center[1]\n    sigma_pixels = max(height, width) * sigma\n    gaussian = np.exp(\n        -((x - center_x) ** 2 + (y - center_y) ** 2) / (2 * sigma_pixels**2),\n    )\n\n    return apply_illumination_pattern(result, gaussian, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_illumination_pattern","title":"def apply_illumination_pattern (img, pattern, intensity) [view source on GitHub]","text":"

Apply illumination pattern to image.

Parameters:

Name Type Description img np.ndarray

Input image

pattern np.ndarray

Illumination pattern of shape (H, W)

intensity float

Effect strength (-0.2 to 0.2)

Returns:

Type Description np.ndarray

Image with applied illumination

Source code in albumentations/augmentations/functional.py Python
def apply_illumination_pattern(\n    img: np.ndarray,\n    pattern: np.ndarray,\n    intensity: float,\n) -> np.ndarray:\n    \"\"\"Apply illumination pattern to image.\n\n    Args:\n        img: Input image\n        pattern: Illumination pattern of shape (H, W)\n        intensity: Effect strength (-0.2 to 0.2)\n\n    Returns:\n        Image with applied illumination\n    \"\"\"\n    if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n        pattern = pattern[..., np.newaxis]\n    return img * (1 + intensity * pattern)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_linear_illumination","title":"def apply_linear_illumination (img, intensity, angle) [view source on GitHub]","text":"

Apply linear gradient illumination effect.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_linear_illumination(\n    img: np.ndarray,\n    intensity: float,\n    angle: float,\n) -> np.ndarray:\n    \"\"\"Apply linear gradient illumination effect.\"\"\"\n    result, height, width = prepare_illumination_input(img)\n\n    # Create gradient coordinates\n    y, x = np.ogrid[:height, :width]\n\n    # Calculate gradient direction\n    angle_rad = np.deg2rad(angle)\n    dx, dy = np.cos(angle_rad), np.sin(angle_rad)\n\n    # Create normalized gradient\n    gradient = (x * dx + y * dy) / np.sqrt(height * height + width * width)\n    gradient = (gradient + 1) / 2  # Normalize to [0, 1]\n\n    return apply_illumination_pattern(result, gradient, intensity)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_plasma_brightness_contrast","title":"def apply_plasma_brightness_contrast (img, brightness_factor, contrast_factor, plasma_pattern) [view source on GitHub]","text":"

Apply plasma-based brightness and contrast adjustments.

The plasma pattern is used to create spatially-varying adjustments: 1. Brightness is modified by adding the pattern * brightness_factor 2. Contrast is modified by interpolating between mean and original using the pattern * contrast_factor

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_plasma_brightness_contrast(\n    img: np.ndarray,\n    brightness_factor: float,\n    contrast_factor: float,\n    plasma_pattern: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply plasma-based brightness and contrast adjustments.\n\n    The plasma pattern is used to create spatially-varying adjustments:\n    1. Brightness is modified by adding the pattern * brightness_factor\n    2. Contrast is modified by interpolating between mean and original\n       using the pattern * contrast_factor\n    \"\"\"\n    result = img.copy()\n\n    max_value = MAX_VALUES_BY_DTYPE[img.dtype]\n\n    # Expand plasma pattern to match image dimensions\n    plasma_pattern = plasma_pattern[..., np.newaxis] if img.ndim > MONO_CHANNEL_DIMENSIONS else plasma_pattern\n\n    # Apply brightness adjustment\n    if brightness_factor != 0:\n        brightness_adjustment = plasma_pattern * brightness_factor * max_value\n        result = np.clip(result + brightness_adjustment, 0, max_value)\n\n    # Apply contrast adjustment\n    if contrast_factor != 0:\n        mean = result.mean()\n        contrast_weights = plasma_pattern * contrast_factor + 1\n        result = np.clip(mean + (result - mean) * contrast_weights, 0, max_value)\n\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_plasma_shadow","title":"def apply_plasma_shadow (img, intensity, plasma_pattern) [view source on GitHub]","text":"

Apply plasma-based shadow effect by darkening.

Parameters:

Name Type Description img np.ndarray

Input image

intensity float

Shadow intensity in [0, 1]

plasma_pattern np.ndarray

Generated plasma pattern of shape (H, W)

Returns:

Type Description np.ndarray

Image with applied shadow effect

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef apply_plasma_shadow(\n    img: np.ndarray,\n    intensity: float,\n    plasma_pattern: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply plasma-based shadow effect by darkening.\n\n    Args:\n        img: Input image\n        intensity: Shadow intensity in [0, 1]\n        plasma_pattern: Generated plasma pattern of shape (H, W)\n\n    Returns:\n        Image with applied shadow effect\n    \"\"\"\n    result = img.copy()\n\n    # Expand dimensions to match image\n    plasma_pattern = plasma_pattern[..., np.newaxis] if img.ndim > MONO_CHANNEL_DIMENSIONS else plasma_pattern\n\n    # Apply shadow by darkening (multiplying by values < 1)\n    shadow_mask = 1 - plasma_pattern * intensity\n\n    return result * shadow_mask\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.apply_salt_and_pepper","title":"def apply_salt_and_pepper (img, salt_mask, pepper_mask) [view source on GitHub]","text":"

Apply salt and pepper noise to image using pre-computed masks.

Parameters:

Name Type Description img np.ndarray

Input image

salt_mask np.ndarray

Boolean mask for salt (white) noise

pepper_mask np.ndarray

Boolean mask for pepper (black) noise

Returns:

Type Description np.ndarray

Image with applied salt and pepper noise

Source code in albumentations/augmentations/functional.py Python
def apply_salt_and_pepper(\n    img: np.ndarray,\n    salt_mask: np.ndarray,\n    pepper_mask: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply salt and pepper noise to image using pre-computed masks.\n\n    Args:\n        img: Input image\n        salt_mask: Boolean mask for salt (white) noise\n        pepper_mask: Boolean mask for pepper (black) noise\n\n    Returns:\n        Image with applied salt and pepper noise\n    \"\"\"\n    result = img.copy()\n\n    result[salt_mask] = MAX_VALUES_BY_DTYPE[img.dtype]\n    result[pepper_mask] = 0\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.auto_contrast","title":"def auto_contrast (img) [view source on GitHub]","text":"

Apply auto contrast to the image.

Auto contrast enhances image contrast by stretching the intensity range to use the full range while preserving relative intensities.

Parameters:

Name Type Description img np.ndarray

Input image in uint8 or float32 format.

Returns:

Type Description np.ndarray

Contrast-enhanced image in the same dtype as input.

Note

The function: 1. Computes histogram for each channel 2. Creates cumulative distribution 3. Normalizes to full intensity range 4. Uses lookup table for scaling

Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef auto_contrast(img: np.ndarray) -> np.ndarray:\n    \"\"\"Apply auto contrast to the image.\n\n    Auto contrast enhances image contrast by stretching the intensity range\n    to use the full range while preserving relative intensities.\n\n    Args:\n        img: Input image in uint8 or float32 format.\n\n    Returns:\n        Contrast-enhanced image in the same dtype as input.\n\n    Note:\n        The function:\n        1. Computes histogram for each channel\n        2. Creates cumulative distribution\n        3. Normalizes to full intensity range\n        4. Uses lookup table for scaling\n    \"\"\"\n    result = img.copy()\n    num_channels = get_num_channels(img)\n    max_value = MAX_VALUES_BY_DTYPE[img.dtype]\n\n    for i in range(num_channels):\n        channel = img[..., i] if img.ndim > MONO_CHANNEL_DIMENSIONS else img\n\n        # Compute histogram\n        hist = np.histogram(channel.flatten(), bins=256, range=(0, max_value))[0]\n\n        # Calculate cumulative distribution\n        cdf = hist.cumsum()\n\n        # Find the minimum and maximum non-zero values in the CDF\n        if cdf[cdf > 0].size == 0:\n            continue  # Skip if the channel is constant or empty\n\n        cdf_min = cdf[cdf > 0].min()\n        cdf_max = cdf.max()\n\n        if cdf_min == cdf_max:\n            continue\n\n        # Normalize CDF\n        cdf = (cdf - cdf_min) * max_value / (cdf_max - cdf_min)\n\n        # Create lookup table\n        lut = np.clip(np.around(cdf), 0, max_value).astype(np.uint8)\n\n        # Apply lookup table\n        if img.ndim > MONO_CHANNEL_DIMENSIONS:\n            result[..., i] = sz_lut(channel, lut)\n        else:\n            result = sz_lut(channel, lut)\n\n    return result\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.clahe","title":"def clahe (img, clip_limit, tile_grid_size) [view source on GitHub]","text":"

Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.

This function enhances the contrast of the input image using CLAHE. For color images, it converts the image to the LAB color space, applies CLAHE to the L channel, and then converts the image back to RGB.

Parameters:

Name Type Description img np.ndarray

Input image. Can be grayscale (2D array) or RGB (3D array).

clip_limit float

Threshold for contrast limiting. Higher values give more contrast.

tile_grid_size tuple[int, int]

Size of grid for histogram equalization. Width and height of the grid.

Returns:

Type Description np.ndarray

Image with CLAHE applied. The output has the same dtype as the input.

Note

  • If the input image is float32, it's temporarily converted to uint8 for processing and then converted back to float32.
  • For color images, CLAHE is applied only to the luminance channel in the LAB color space.

Exceptions:

Type Description ValueError

If the input image is not 2D or 3D.

Examples:

Python
>>> import numpy as np\n>>> img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> result = clahe(img, clip_limit=2.0, tile_grid_size=(8, 8))\n>>> assert result.shape == img.shape\n>>> assert result.dtype == img.dtype\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef clahe(\n    img: np.ndarray,\n    clip_limit: float,\n    tile_grid_size: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.\n\n    This function enhances the contrast of the input image using CLAHE. For color images,\n    it converts the image to the LAB color space, applies CLAHE to the L channel, and then\n    converts the image back to RGB.\n\n    Args:\n        img (np.ndarray): Input image. Can be grayscale (2D array) or RGB (3D array).\n        clip_limit (float): Threshold for contrast limiting. Higher values give more contrast.\n        tile_grid_size (tuple[int, int]): Size of grid for histogram equalization.\n            Width and height of the grid.\n\n    Returns:\n        np.ndarray: Image with CLAHE applied. The output has the same dtype as the input.\n\n    Note:\n        - If the input image is float32, it's temporarily converted to uint8 for processing\n          and then converted back to float32.\n        - For color images, CLAHE is applied only to the luminance channel in the LAB color space.\n\n    Raises:\n        ValueError: If the input image is not 2D or 3D.\n\n    Example:\n        >>> import numpy as np\n        >>> img = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> result = clahe(img, clip_limit=2.0, tile_grid_size=(8, 8))\n        >>> assert result.shape == img.shape\n        >>> assert result.dtype == img.dtype\n    \"\"\"\n    img = img.copy()\n    clahe_mat = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)\n\n    if is_grayscale_image(img):\n        return clahe_mat.apply(img)\n\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)\n\n    img[:, :, 0] = clahe_mat.apply(img[:, :, 0])\n\n    return cv2.cvtColor(img, cv2.COLOR_LAB2RGB)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.diamond_step","title":"def diamond_step (pattern, y, x, half, grid_size, roughness, random_generator) [view source on GitHub]","text":"

Compute edge value during diamond step.

Source code in albumentations/augmentations/functional.py Python
def diamond_step(\n    pattern: np.ndarray,\n    y: int,\n    x: int,\n    half: int,\n    grid_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Compute edge value during diamond step.\"\"\"\n    points = []\n    if y >= half:\n        points.append(pattern[y - half, x])\n    if y + half <= grid_size:\n        points.append(pattern[y + half, x])\n    if x >= half:\n        points.append(pattern[y, x - half])\n    if x + half <= grid_size:\n        points.append(pattern[y, x + half])\n\n    return sum(points) / len(points) + random_offset(\n        half * 2,\n        grid_size,\n        roughness,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.equalize","title":"def equalize (img, mask=None, mode='cv', by_channels=True) [view source on GitHub]","text":"

Apply histogram equalization to the input image.

This function enhances the contrast of the input image by equalizing its histogram. It supports both grayscale and color images, and can operate on individual channels or on the luminance channel of the image.

Parameters:

Name Type Description img np.ndarray

Input image. Can be grayscale (2D array) or RGB (3D array).

mask np.ndarray | None

Optional mask to apply the equalization selectively. If provided, must have the same shape as the input image. Default: None.

mode ImageMode

The backend to use for equalization. Can be either \"cv\" for OpenCV or \"pil\" for Pillow-style equalization. Default: \"cv\".

by_channels bool

If True, applies equalization to each channel independently. If False, converts the image to YCrCb color space and equalizes only the luminance channel. Only applicable to color images. Default: True.

Returns:

Type Description np.ndarray

Equalized image. The output has the same dtype as the input.

Exceptions:

Type Description ValueError

If the input image or mask have invalid shapes or types.

Note

  • If the input image is not uint8, it will be temporarily converted to uint8 for processing and then converted back to its original dtype.
  • For color images, when by_channels=False, the image is converted to YCrCb color space, equalized on the Y channel, and then converted back to RGB.
  • The function preserves the original number of channels in the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> equalized = A.equalize(image, mode=\"cv\", by_channels=True)\n>>> assert equalized.shape == image.shape\n>>> assert equalized.dtype == image.dtype\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef equalize(\n    img: np.ndarray,\n    mask: np.ndarray | None = None,\n    mode: ImageMode = \"cv\",\n    by_channels: bool = True,\n) -> np.ndarray:\n    \"\"\"Apply histogram equalization to the input image.\n\n    This function enhances the contrast of the input image by equalizing its histogram.\n    It supports both grayscale and color images, and can operate on individual channels\n    or on the luminance channel of the image.\n\n    Args:\n        img (np.ndarray): Input image. Can be grayscale (2D array) or RGB (3D array).\n        mask (np.ndarray | None): Optional mask to apply the equalization selectively.\n            If provided, must have the same shape as the input image. Default: None.\n        mode (ImageMode): The backend to use for equalization. Can be either \"cv\" for\n            OpenCV or \"pil\" for Pillow-style equalization. Default: \"cv\".\n        by_channels (bool): If True, applies equalization to each channel independently.\n            If False, converts the image to YCrCb color space and equalizes only the\n            luminance channel. Only applicable to color images. Default: True.\n\n    Returns:\n        np.ndarray: Equalized image. The output has the same dtype as the input.\n\n    Raises:\n        ValueError: If the input image or mask have invalid shapes or types.\n\n    Note:\n        - If the input image is not uint8, it will be temporarily converted to uint8\n          for processing and then converted back to its original dtype.\n        - For color images, when by_channels=False, the image is converted to YCrCb\n          color space, equalized on the Y channel, and then converted back to RGB.\n        - The function preserves the original number of channels in the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> equalized = A.equalize(image, mode=\"cv\", by_channels=True)\n        >>> assert equalized.shape == image.shape\n        >>> assert equalized.dtype == image.dtype\n    \"\"\"\n    _check_preconditions(img, mask, by_channels)\n\n    function = _equalize_pil if mode == \"pil\" else _equalize_cv\n\n    if is_grayscale_image(img):\n        return function(img, _handle_mask(mask))\n\n    if not by_channels:\n        result_img = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)\n        result_img[..., 0] = function(result_img[..., 0], _handle_mask(mask))\n        return cv2.cvtColor(result_img, cv2.COLOR_YCrCb2RGB)\n\n    result_img = np.empty_like(img)\n    for i in range(NUM_RGB_CHANNELS):\n        _mask = _handle_mask(mask, i)\n        result_img[..., i] = function(img[..., i], _mask)\n\n    return result_img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.fancy_pca","title":"def fancy_pca (img, alpha_vector) [view source on GitHub]","text":"

Perform 'Fancy PCA' augmentation on an image with any number of channels.

Parameters:

Name Type Description img np.ndarray

Input image

alpha_vector np.ndarray

Vector of scale factors for each principal component. Should have the same length as the number of channels in the image.

Returns:

Type Description np.ndarray

Augmented image of the same shape, type, and range as the input.

Image types: uint8, float32

Number of channels: Any

Note

  • This function generalizes the Fancy PCA augmentation to work with any number of channels.
  • It preserves the original range of the image ([0, 255] for uint8, [0, 1] for float32).
  • For single-channel images, the augmentation is applied as a simple scaling of pixel intensity variation.
  • For multi-channel images, PCA is performed on the entire image, treating each pixel as a point in N-dimensional space (where N is the number of channels).
  • The augmentation preserves the correlation between channels while adding controlled noise.
  • Computation time may increase significantly for images with a large number of channels.

Reference

Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).

Source code in albumentations/augmentations/functional.py Python
@float32_io\n@clipped\n@preserve_channel_dim\ndef fancy_pca(img: np.ndarray, alpha_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Perform 'Fancy PCA' augmentation on an image with any number of channels.\n\n    Args:\n        img (np.ndarray): Input image\n        alpha_vector (np.ndarray): Vector of scale factors for each principal component.\n                                   Should have the same length as the number of channels in the image.\n\n    Returns:\n        np.ndarray: Augmented image of the same shape, type, and range as the input.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This function generalizes the Fancy PCA augmentation to work with any number of channels.\n        - It preserves the original range of the image ([0, 255] for uint8, [0, 1] for float32).\n        - For single-channel images, the augmentation is applied as a simple scaling of pixel intensity variation.\n        - For multi-channel images, PCA is performed on the entire image, treating each pixel\n          as a point in N-dimensional space (where N is the number of channels).\n        - The augmentation preserves the correlation between channels while adding controlled noise.\n        - Computation time may increase significantly for images with a large number of channels.\n\n    Reference:\n        Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012).\n        ImageNet classification with deep convolutional neural networks.\n        In Advances in neural information processing systems (pp. 1097-1105).\n    \"\"\"\n    orig_shape = img.shape\n    num_channels = get_num_channels(img)\n\n    # Reshape image to 2D array of pixels\n    img_reshaped = img.reshape(-1, num_channels)\n\n    # Center the pixel values\n    img_mean = np.mean(img_reshaped, axis=0)\n    img_centered = img_reshaped - img_mean\n\n    if num_channels == 1:\n        # For grayscale images, apply a simple scaling\n        std_dev = np.std(img_centered)\n        noise = alpha_vector[0] * std_dev * img_centered\n    else:\n        # Compute covariance matrix\n        img_cov = np.cov(img_centered, rowvar=False)\n\n        # Compute eigenvectors & eigenvalues of the covariance matrix\n        eig_vals, eig_vecs = np.linalg.eigh(img_cov)\n\n        # Sort eigenvectors by eigenvalues in descending order\n        sort_perm = eig_vals[::-1].argsort()\n        eig_vals = eig_vals[sort_perm]\n        eig_vecs = eig_vecs[:, sort_perm]\n\n        # Create noise vector\n        noise = np.dot(\n            np.dot(eig_vecs, np.diag(alpha_vector * eig_vals)),\n            img_centered.T,\n        ).T\n\n    # Add noise to the image\n    img_pca = img_reshaped + noise\n\n    # Reshape back to original shape\n    img_pca = img_pca.reshape(orig_shape)\n\n    # Clip values to [0, 1] range\n    return np.clip(img_pca, 0, 1, out=img_pca)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_constant_noise","title":"def generate_constant_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate one value per channel.

Source code in albumentations/augmentations/functional.py Python
def generate_constant_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate one value per channel.\"\"\"\n    num_channels = shape[-1] if len(shape) > MONO_CHANNEL_DIMENSIONS else 1\n    return sample_noise(\n        noise_type,\n        (num_channels,),\n        params,\n        max_value,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_per_pixel_noise","title":"def generate_per_pixel_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate separate noise map for each channel.

Source code in albumentations/augmentations/functional.py Python
def generate_per_pixel_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate separate noise map for each channel.\"\"\"\n    return sample_noise(noise_type, shape, params, max_value, random_generator)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_plasma_pattern","title":"def generate_plasma_pattern (target_shape, size, roughness, random_generator) [view source on GitHub]","text":"

Generate a plasma fractal pattern using the Diamond-Square algorithm.

The Diamond-Square algorithm creates a natural-looking noise pattern by recursively subdividing a grid and adding random displacements at each step. The roughness parameter controls how quickly the random displacements decrease with each iteration.

Parameters:

Name Type Description target_shape tuple[int, int]

Final shape (height, width) of the pattern

size int

Initial size of the pattern grid. Will be rounded up to nearest power of 2. Larger values create more detailed patterns.

roughness float

Controls pattern roughness. Higher values create more rough/sharp transitions. Typical values are between 1.0 and 5.0.

random_generator np.random.Generator

NumPy random generator.

Returns:

Type Description np.ndarray

Normalized plasma pattern array of shape target_shape with values in [0, 1]

Source code in albumentations/augmentations/functional.py Python
def generate_plasma_pattern(\n    target_shape: tuple[int, int],\n    size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a plasma fractal pattern using the Diamond-Square algorithm.\n\n    The Diamond-Square algorithm creates a natural-looking noise pattern by recursively\n    subdividing a grid and adding random displacements at each step. The roughness\n    parameter controls how quickly the random displacements decrease with each iteration.\n\n    Args:\n        target_shape: Final shape (height, width) of the pattern\n        size: Initial size of the pattern grid. Will be rounded up to nearest power of 2.\n            Larger values create more detailed patterns.\n        roughness: Controls pattern roughness. Higher values create more rough/sharp transitions.\n            Typical values are between 1.0 and 5.0.\n        random_generator: NumPy random generator.\n\n    Returns:\n        Normalized plasma pattern array of shape target_shape with values in [0, 1]\n    \"\"\"\n    # Initialize grid\n    grid_size = get_grid_size(size, target_shape)\n    pattern = initialize_grid(grid_size, random_generator)\n\n    # Diamond-Square algorithm\n    step_size = grid_size\n    while step_size > 1:\n        half_step = step_size // 2\n\n        # Square step\n        for y in range(0, grid_size, step_size):\n            for x in range(0, grid_size, step_size):\n                if half_step > 0:\n                    pattern[y + half_step, x + half_step] = square_step(\n                        pattern,\n                        y,\n                        x,\n                        step_size,\n                        half_step,\n                        roughness,\n                        random_generator,\n                    )\n\n        # Diamond step\n        for y in range(0, grid_size + 1, half_step):\n            for x in range((y + half_step) % step_size, grid_size + 1, step_size):\n                pattern[y, x] = diamond_step(\n                    pattern,\n                    y,\n                    x,\n                    half_step,\n                    grid_size,\n                    roughness,\n                    random_generator,\n                )\n\n        step_size = half_step\n\n    min_pattern = pattern.min()\n\n    # Normalize to [0, 1] range\n    pattern = (pattern - min_pattern) / (pattern.max() - min_pattern)\n\n    return (\n        fgeometric.resize(pattern, target_shape, interpolation=cv2.INTER_LINEAR)\n        if pattern.shape != target_shape\n        else pattern\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_shared_noise","title":"def generate_shared_noise (noise_type, shape, params, max_value, random_generator) [view source on GitHub]","text":"

Generate one noise map and broadcast to all channels.

Parameters:

Name Type Description noise_type Literal['uniform', 'gaussian', 'laplace', 'beta']

Type of noise distribution to use

shape tuple[int, ...]

Shape of the input image (H, W) or (H, W, C)

params dict[str, Any]

Parameters for the noise distribution

max_value float

Maximum value for the noise distribution

random_generator np.random.Generator

NumPy random generator instance

Returns:

Type Description np.ndarray

Noise array of shape (H, W) or (H, W, C) where the same noise pattern is shared across all channels

Source code in albumentations/augmentations/functional.py Python
def generate_shared_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    shape: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate one noise map and broadcast to all channels.\n\n    Args:\n        noise_type: Type of noise distribution to use\n        shape: Shape of the input image (H, W) or (H, W, C)\n        params: Parameters for the noise distribution\n        max_value: Maximum value for the noise distribution\n        random_generator: NumPy random generator instance\n\n    Returns:\n        Noise array of shape (H, W) or (H, W, C) where the same noise\n        pattern is shared across all channels\n    \"\"\"\n    # Generate noise for (H, W)\n    height, width = shape[:2]\n    noise_map = sample_noise(\n        noise_type,\n        (height, width),\n        params,\n        max_value,\n        random_generator,\n    )\n\n    # If input is multichannel, broadcast noise to all channels\n    if len(shape) > MONO_CHANNEL_DIMENSIONS:\n        return np.broadcast_to(noise_map[..., None], shape)\n    return noise_map\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.generate_snow_textures","title":"def generate_snow_textures (img_shape, random_generator) [view source on GitHub]","text":"

Generate snow texture and sparkle mask.

Parameters:

Name Type Description img_shape tuple[int, int]

Image shape.

random_generator np.random.Generator

Random generator to use.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

Tuple of (snow_texture, sparkle_mask) arrays.

Source code in albumentations/augmentations/functional.py Python
def generate_snow_textures(\n    img_shape: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate snow texture and sparkle mask.\n\n    Args:\n        img_shape (tuple[int, int]): Image shape.\n        random_generator (np.random.Generator): Random generator to use.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: Tuple of (snow_texture, sparkle_mask) arrays.\n    \"\"\"\n    # Generate base snow texture\n    snow_texture = random_generator.normal(size=img_shape[:2], loc=0.5, scale=0.3)\n    snow_texture = cv2.GaussianBlur(snow_texture, (0, 0), sigmaX=1, sigmaY=1)\n\n    # Generate sparkle mask\n    sparkle_mask = random_generator.random(img_shape[:2]) > 0.99\n\n    return snow_texture, sparkle_mask\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_fog_particle_radiuses","title":"def get_fog_particle_radiuses (img_shape, num_particles, fog_intensity, random_generator) [view source on GitHub]","text":"

Generate radiuses for fog particles.

Parameters:

Name Type Description img_shape tuple[int, int]

Image shape.

num_particles int

Number of fog particles.

fog_intensity float

Intensity of the fog effect, between 0 and 1.

random_generator np.random.Generator

Random generator to use.

Returns:

Type Description list[int]

List of radiuses for each fog particle.

Source code in albumentations/augmentations/functional.py Python
def get_fog_particle_radiuses(\n    img_shape: tuple[int, int],\n    num_particles: int,\n    fog_intensity: float,\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Generate radiuses for fog particles.\n\n    Args:\n        img_shape (tuple[int, int]): Image shape.\n        num_particles (int): Number of fog particles.\n        fog_intensity (float): Intensity of the fog effect, between 0 and 1.\n        random_generator (np.random.Generator): Random generator to use.\n\n    Returns:\n        list[int]: List of radiuses for each fog particle.\n    \"\"\"\n    height, width = img_shape[:2]\n    max_fog_radius = max(2, int(min(height, width) * 0.1 * fog_intensity))\n    min_radius = max(1, max_fog_radius // 2)\n\n    return [random_generator.integers(min_radius, max_fog_radius) for _ in range(num_particles)]\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_grid_size","title":"def get_grid_size (size, target_shape) [view source on GitHub]","text":"

Round up to nearest power of 2.

Source code in albumentations/augmentations/functional.py Python
def get_grid_size(size: int, target_shape: tuple[int, int]) -> int:\n    \"\"\"Round up to nearest power of 2.\"\"\"\n    return 2 ** int(np.ceil(np.log2(max(size, *target_shape))))\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.get_safe_brightness_contrast_params","title":"def get_safe_brightness_contrast_params (alpha, beta, max_value) [view source on GitHub]","text":"

Calculate safe alpha and beta values to prevent overflow/underflow.

For any pixel value x, we want: 0 <= alpha * x + beta <= max_value

Parameters:

Name Type Description alpha float

Contrast factor (1 means no change)

beta float

Brightness offset

max_value float

Maximum allowed value (255 for uint8, 1 for float32)

Returns:

Type Description tuple[float, float]

Safe (alpha, beta) values that prevent overflow/underflow

Source code in albumentations/augmentations/functional.py Python
def get_safe_brightness_contrast_params(\n    alpha: float,\n    beta: float,\n    max_value: float,\n) -> tuple[float, float]:\n    \"\"\"Calculate safe alpha and beta values to prevent overflow/underflow.\n\n    For any pixel value x, we want: 0 <= alpha * x + beta <= max_value\n\n    Args:\n        alpha: Contrast factor (1 means no change)\n        beta: Brightness offset\n        max_value: Maximum allowed value (255 for uint8, 1 for float32)\n\n    Returns:\n        tuple[float, float]: Safe (alpha, beta) values that prevent overflow/underflow\n    \"\"\"\n    if alpha > 0:\n        # For x = max_value: alpha * max_value + beta <= max_value\n        # For x = 0: beta >= 0\n        safe_beta = np.clip(beta, 0, max_value)\n        # From alpha * max_value + safe_beta <= max_value\n        safe_alpha = min(alpha, (max_value - safe_beta) / max_value)\n    else:\n        # For x = 0: beta <= max_value\n        # For x = max_value: alpha * max_value + beta >= 0\n        safe_beta = min(beta, max_value)\n        # From alpha * max_value + safe_beta >= 0\n        safe_alpha = max(alpha, -safe_beta / max_value)\n\n    return safe_alpha, safe_beta\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.grayscale_to_multichannel","title":"def grayscale_to_multichannel (grayscale_image, num_output_channels=3) [view source on GitHub]","text":"

Convert a grayscale image to a multi-channel image.

This function takes a 2D grayscale image or a 3D image with a single channel and converts it to a multi-channel image by repeating the grayscale data across the specified number of channels.

Parameters:

Name Type Description grayscale_image np.ndarray

Input grayscale image. Can be 2D (height, width) or 3D (height, width, 1).

num_output_channels int

Number of channels in the output image. Defaults to 3.

Returns:

Type Description np.ndarray

Multi-channel image with shape (height, width, num_channels)

Source code in albumentations/augmentations/functional.py Python
def grayscale_to_multichannel(\n    grayscale_image: np.ndarray,\n    num_output_channels: int = 3,\n) -> np.ndarray:\n    \"\"\"Convert a grayscale image to a multi-channel image.\n\n    This function takes a 2D grayscale image or a 3D image with a single channel\n    and converts it to a multi-channel image by repeating the grayscale data\n    across the specified number of channels.\n\n    Args:\n        grayscale_image (np.ndarray): Input grayscale image. Can be 2D (height, width)\n                                      or 3D (height, width, 1).\n        num_output_channels (int, optional): Number of channels in the output image. Defaults to 3.\n\n    Returns:\n        np.ndarray: Multi-channel image with shape (height, width, num_channels)\n    \"\"\"\n    # If output should be single channel, just squeeze and return\n    if num_output_channels == 1:\n        return grayscale_image\n\n    # For multi-channel output, squeeze and stack\n    squeezed = np.squeeze(grayscale_image)\n\n    return cv2.merge([squeezed] * num_output_channels)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.image_compression","title":"def image_compression (img, quality, image_type) [view source on GitHub]","text":"

Apply compression to image.

Parameters:

Name Type Description img np.ndarray

Input image

quality int

Compression quality (0-100)

image_type Literal['.jpg', '.webp']

Type of compression ('.jpg' or '.webp')

Returns:

Type Description np.ndarray

Compressed image with same number of channels as input

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef image_compression(\n    img: np.ndarray,\n    quality: int,\n    image_type: Literal[\".jpg\", \".webp\"],\n) -> np.ndarray:\n    \"\"\"Apply compression to image.\n\n    Args:\n        img: Input image\n        quality: Compression quality (0-100)\n        image_type: Type of compression ('.jpg' or '.webp')\n\n    Returns:\n        Compressed image with same number of channels as input\n    \"\"\"\n    quality_flag = cv2.IMWRITE_JPEG_QUALITY if image_type == \".jpg\" else cv2.IMWRITE_WEBP_QUALITY\n\n    num_channels = get_num_channels(img)\n\n    if num_channels == 1:\n        # For grayscale, ensure we read back as single channel\n        _, encoded_img = cv2.imencode(image_type, img, (int(quality_flag), quality))\n        decoded = cv2.imdecode(encoded_img, cv2.IMREAD_GRAYSCALE)\n        return decoded[..., np.newaxis]  # Add channel dimension back\n\n    if num_channels == NUM_RGB_CHANNELS:\n        # Standard RGB image\n        _, encoded_img = cv2.imencode(image_type, img, (int(quality_flag), quality))\n        return cv2.imdecode(encoded_img, cv2.IMREAD_UNCHANGED)\n\n    # For 2,4 or more channels, we need to handle alpha/extra channels separately\n    if num_channels == 2:\n        # For 2 channels, pad to 3 channels and take only first 2 after compression\n        padded = np.pad(img, ((0, 0), (0, 0), (0, 1)), mode=\"constant\")\n        _, encoded_bgr = cv2.imencode(image_type, padded, (int(quality_flag), quality))\n        decoded_bgr = cv2.imdecode(encoded_bgr, cv2.IMREAD_UNCHANGED)\n        return decoded_bgr[..., :2]\n\n    # Process first 3 channels together\n    bgr = img[..., :NUM_RGB_CHANNELS]\n    _, encoded_bgr = cv2.imencode(image_type, bgr, (int(quality_flag), quality))\n    decoded_bgr = cv2.imdecode(encoded_bgr, cv2.IMREAD_UNCHANGED)\n\n    if num_channels > NUM_RGB_CHANNELS:\n        # Process additional channels one by one\n        extra_channels = []\n        for i in range(NUM_RGB_CHANNELS, num_channels):\n            channel = img[..., i]\n            _, encoded = cv2.imencode(image_type, channel, (int(quality_flag), quality))\n            decoded = cv2.imdecode(encoded, cv2.IMREAD_GRAYSCALE)\n            if len(decoded.shape) == 2:\n                decoded = decoded[..., np.newaxis]\n            extra_channels.append(decoded)\n\n        # Combine BGR with extra channels\n        return np.dstack([decoded_bgr, *extra_channels])\n\n    return decoded_bgr\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.initialize_grid","title":"def initialize_grid (grid_size, random_generator) [view source on GitHub]","text":"

Initialize grid with random corners.

Source code in albumentations/augmentations/functional.py Python
def initialize_grid(\n    grid_size: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Initialize grid with random corners.\"\"\"\n    pattern = np.zeros((grid_size + 1, grid_size + 1), dtype=np.float32)\n    for corner in [(0, 0), (0, -1), (-1, 0), (-1, -1)]:\n        pattern[corner] = random_generator.random()\n    return pattern\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.iso_noise","title":"def iso_noise (image, color_shift, intensity, random_generator) [view source on GitHub]","text":"

Apply poisson noise to an image to simulate camera sensor noise.

Parameters:

Name Type Description image np.ndarray

Input image. Currently, only RGB images are supported.

color_shift float

The amount of color shift to apply.

intensity float

Multiplication factor for noise values. Values of ~0.5 produce a noticeable, yet acceptable level of noise.

random_generator np.random.Generator

If specified, this will be random generator used for noise generation.

Returns:

Type Description np.ndarray

The noised image.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
@float32_io\n@clipped\ndef iso_noise(\n    image: np.ndarray,\n    color_shift: float,\n    intensity: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply poisson noise to an image to simulate camera sensor noise.\n\n    Args:\n        image (np.ndarray): Input image. Currently, only RGB images are supported.\n        color_shift (float): The amount of color shift to apply.\n        intensity (float): Multiplication factor for noise values. Values of ~0.5 produce a noticeable,\n                           yet acceptable level of noise.\n        random_generator (np.random.Generator): If specified, this will be random generator used\n            for noise generation.\n\n    Returns:\n        np.ndarray: The noised image.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    hls = cv2.cvtColor(image, cv2.COLOR_RGB2HLS)\n    _, stddev = cv2.meanStdDev(hls)\n\n    luminance_noise = random_generator.poisson(\n        stddev[1] * intensity,\n        size=hls.shape[:2],\n    )\n    color_noise = random_generator.normal(\n        0,\n        color_shift * intensity,\n        size=hls.shape[:2],\n    )\n\n    hls[..., 0] += color_noise\n    hls[..., 1] = add_array(\n        hls[..., 1],\n        luminance_noise * intensity * (1.0 - hls[..., 1]),\n    )\n\n    noised_hls = cv2.cvtColor(hls, cv2.COLOR_HLS2RGB)\n    return np.clip(noised_hls, 0, 1, out=noised_hls)  # Ensure output is in [0, 1] range\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.move_tone_curve","title":"def move_tone_curve (img, low_y, high_y) [view source on GitHub]","text":"

Rescales the relationship between bright and dark areas of the image by manipulating its tone curve.

Parameters:

Name Type Description img np.ndarray

np.ndarray. Any number of channels

low_y float | np.ndarray

per-channel or single y-position of a Bezier control point used to adjust the tone curve, must be in range [0, 1]

high_y float | np.ndarray

per-channel or single y-position of a Bezier control point used to adjust image tone curve, must be in range [0, 1]

Source code in albumentations/augmentations/functional.py Python
@uint8_io\ndef move_tone_curve(\n    img: np.ndarray,\n    low_y: float | np.ndarray,\n    high_y: float | np.ndarray,\n) -> np.ndarray:\n    \"\"\"Rescales the relationship between bright and dark areas of the image by manipulating its tone curve.\n\n    Args:\n        img: np.ndarray. Any number of channels\n        low_y: per-channel or single y-position of a Bezier control point used\n            to adjust the tone curve, must be in range [0, 1]\n        high_y: per-channel or single y-position of a Bezier control point used\n            to adjust image tone curve, must be in range [0, 1]\n\n    \"\"\"\n    t = np.linspace(0.0, 1.0, 256)\n\n    def evaluate_bez(\n        t: np.ndarray,\n        low_y: float | np.ndarray,\n        high_y: float | np.ndarray,\n    ) -> np.ndarray:\n        one_minus_t = 1 - t\n        return (3 * one_minus_t**2 * t * low_y + 3 * one_minus_t * t**2 * high_y + t**3) * 255\n\n    num_channels = get_num_channels(img)\n\n    if np.isscalar(low_y) and np.isscalar(high_y):\n        lut = clip(np.rint(evaluate_bez(t, low_y, high_y)), np.uint8, inplace=False)\n        return sz_lut(img, lut, inplace=False)\n    if isinstance(low_y, np.ndarray) and isinstance(high_y, np.ndarray):\n        luts = clip(\n            np.rint(evaluate_bez(t[:, np.newaxis], low_y, high_y).T),\n            np.uint8,\n            inplace=False,\n        )\n        return cv2.merge(\n            [sz_lut(img[:, :, i], np.ascontiguousarray(luts[i]), inplace=False) for i in range(num_channels)],\n        )\n\n    raise TypeError(\n        f\"low_y and high_y must both be of type float or np.ndarray. Got {type(low_y)} and {type(high_y)}\",\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.posterize","title":"def posterize (img, bits) [view source on GitHub]","text":"

Reduce the number of bits for each color channel by keeping only the highest N bits.

This transform performs bit-depth reduction by masking out lower bits, effectively reducing the number of possible values per channel. This creates a posterization effect where similar colors are merged together.

Parameters:

Name Type Description img np.ndarray

Input image. Can be single or multi-channel.

bits Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]]

Number of high bits to keep. Must be in range [1, 7]. Can be either: - A single value to apply the same bit reduction to all channels - A list of values to apply different bit reduction per channel. Length of list must match number of channels in image.

Returns:

Type Description np.ndarray

Image with reduced bit depth. Has same shape and dtype as input.

Note

  • The transform keeps the N highest bits and sets all other bits to 0
  • For example, if bits=3:
    • Original value: 11010110 (214)
    • Keep 3 bits: 11000000 (192)
  • The number of unique colors per channel will be 2^bits
  • Higher bits values = more colors = more subtle effect
  • Lower bits values = fewer colors = more dramatic posterization

Examples:

Python
>>> import numpy as np\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Same posterization for all channels\n>>> result = posterize(image, bits=3)\n>>> # Different posterization per channel\n>>> result = posterize(image, bits=[3, 4, 5])  # RGB channels\n
Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef posterize(img: np.ndarray, bits: Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]]) -> np.ndarray:\n    \"\"\"Reduce the number of bits for each color channel by keeping only the highest N bits.\n\n    This transform performs bit-depth reduction by masking out lower bits, effectively\n    reducing the number of possible values per channel. This creates a posterization\n    effect where similar colors are merged together.\n\n    Args:\n        img: Input image. Can be single or multi-channel.\n        bits: Number of high bits to keep. Must be in range [1, 7].\n            Can be either:\n            - A single value to apply the same bit reduction to all channels\n            - A list of values to apply different bit reduction per channel.\n              Length of list must match number of channels in image.\n\n    Returns:\n        np.ndarray: Image with reduced bit depth. Has same shape and dtype as input.\n\n    Note:\n        - The transform keeps the N highest bits and sets all other bits to 0\n        - For example, if bits=3:\n            - Original value: 11010110 (214)\n            - Keep 3 bits:   11000000 (192)\n        - The number of unique colors per channel will be 2^bits\n        - Higher bits values = more colors = more subtle effect\n        - Lower bits values = fewer colors = more dramatic posterization\n\n    Examples:\n        >>> import numpy as np\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Same posterization for all channels\n        >>> result = posterize(image, bits=3)\n        >>> # Different posterization per channel\n        >>> result = posterize(image, bits=[3, 4, 5])  # RGB channels\n    \"\"\"\n    bits_array = np.uint8(bits)\n\n    if not bits_array.shape or len(bits_array) == 1:\n        lut = np.arange(0, 256, dtype=np.uint8)\n        mask = ~np.uint8(2 ** (8 - bits_array) - 1)\n        lut &= mask\n\n        return sz_lut(img, lut, inplace=False)\n\n    result_img = np.empty_like(img)\n    for i, channel_bits in enumerate(bits_array):\n        lut = np.arange(0, 256, dtype=np.uint8)\n        mask = ~np.uint8(2 ** (8 - channel_bits) - 1)\n        lut &= mask\n\n        result_img[..., i] = sz_lut(img[..., i], lut, inplace=True)\n\n    return result_img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.prepare_illumination_input","title":"def prepare_illumination_input (img) [view source on GitHub]","text":"

Prepare image for illumination effect.

Parameters:

Name Type Description img np.ndarray

Input image

Returns:

Type Description tuple of
  • float32 image
  • height
  • width
Source code in albumentations/augmentations/functional.py Python
def prepare_illumination_input(img: np.ndarray) -> tuple[np.ndarray, int, int]:\n    \"\"\"Prepare image for illumination effect.\n\n    Args:\n        img: Input image\n\n    Returns:\n        tuple of:\n        - float32 image\n        - height\n        - width\n    \"\"\"\n    result = img.astype(np.float32)\n    height, width = img.shape[:2]\n    return result, height, width\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.random_offset","title":"def random_offset (current_size, total_size, roughness, random_generator) [view source on GitHub]","text":"

Calculate random offset based on current grid size.

Source code in albumentations/augmentations/functional.py Python
def random_offset(\n    current_size: int,\n    total_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Calculate random offset based on current grid size.\"\"\"\n    return (random_generator.random() - 0.5) * (current_size / total_size) ** (roughness / 2)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_beta","title":"def sample_beta (size, params, random_generator) [view source on GitHub]","text":"

Sample from Beta distribution.

The Beta distribution is bounded by [0, 1] and then scaled and shifted to [-scale, scale]. Alpha and beta parameters control the shape of the distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_beta(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Beta distribution.\n\n    The Beta distribution is bounded by [0, 1] and then scaled and shifted to [-scale, scale].\n    Alpha and beta parameters control the shape of the distribution.\n    \"\"\"\n    alpha = random_generator.uniform(*params[\"alpha_range\"])\n    beta = random_generator.uniform(*params[\"beta_range\"])\n    scale = random_generator.uniform(*params[\"scale_range\"])\n\n    # Sample from Beta[0,1] and transform to [-scale,scale]\n    samples = random_generator.beta(alpha, beta, size=size)\n    return (2 * samples - 1) * scale\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_gaussian","title":"def sample_gaussian (size, params, random_generator) [view source on GitHub]","text":"

Sample from Gaussian distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_gaussian(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Gaussian distribution.\"\"\"\n    mean = random_generator.uniform(*params[\"mean_range\"])\n    std = random_generator.uniform(*params[\"std_range\"])\n    return random_generator.normal(mean, std, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_laplace","title":"def sample_laplace (size, params, random_generator) [view source on GitHub]","text":"

Sample from Laplace distribution.

The Laplace distribution is also known as the double exponential distribution. It has heavier tails than the Gaussian distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_laplace(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from Laplace distribution.\n\n    The Laplace distribution is also known as the double exponential distribution.\n    It has heavier tails than the Gaussian distribution.\n    \"\"\"\n    loc = random_generator.uniform(*params[\"mean_range\"])\n    scale = random_generator.uniform(*params[\"scale_range\"])\n    return random_generator.laplace(loc=loc, scale=scale, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_noise","title":"def sample_noise (noise_type, size, params, max_value, random_generator) [view source on GitHub]","text":"

Sample from specific noise distribution.

Source code in albumentations/augmentations/functional.py Python
def sample_noise(\n    noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"],\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    max_value: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Sample from specific noise distribution.\"\"\"\n    if noise_type == \"uniform\":\n        return sample_uniform(size, params, random_generator) * max_value\n    if noise_type == \"gaussian\":\n        return sample_gaussian(size, params, random_generator) * max_value\n    if noise_type == \"laplace\":\n        return sample_laplace(size, params, random_generator) * max_value\n    if noise_type == \"beta\":\n        return sample_beta(size, params, random_generator) * max_value\n\n    raise ValueError(f\"Unknown noise type: {noise_type}\")\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sample_uniform","title":"def sample_uniform (size, params, random_generator) [view source on GitHub]","text":"

Sample from uniform distribution.

Parameters:

Name Type Description size tuple[int, ...]

Output shape. If length is 1, generates constant noise per channel.

params dict[str, Any]

Must contain 'ranges' key with list of (min, max) tuples. If only one range is provided, it will be used for all channels.

random_generator np.random.Generator

NumPy random generator instance

Returns:

Type Description np.ndarray | float

Noise array of specified size. For single-channel constant mode, returns scalar instead of array with shape (1,).

Source code in albumentations/augmentations/functional.py Python
def sample_uniform(\n    size: tuple[int, ...],\n    params: dict[str, Any],\n    random_generator: np.random.Generator,\n) -> np.ndarray | float:\n    \"\"\"Sample from uniform distribution.\n\n    Args:\n        size: Output shape. If length is 1, generates constant noise per channel.\n        params: Must contain 'ranges' key with list of (min, max) tuples.\n            If only one range is provided, it will be used for all channels.\n        random_generator: NumPy random generator instance\n\n    Returns:\n        Noise array of specified size. For single-channel constant mode,\n        returns scalar instead of array with shape (1,).\n    \"\"\"\n    if len(size) == 1:  # constant mode\n        ranges = params[\"ranges\"]\n        num_channels = size[0]\n\n        if len(ranges) == 1:\n            ranges = ranges * num_channels\n        elif len(ranges) < num_channels:\n            raise ValueError(\n                f\"Not enough ranges provided. Expected {num_channels}, got {len(ranges)}\",\n            )\n\n        return np.array(\n            [random_generator.uniform(low, high) for low, high in ranges[:num_channels]],\n        )\n\n    # use first range for spatial noise\n    low, high = params[\"ranges\"][0]\n    return random_generator.uniform(low, high, size=size)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.sharpen_gaussian","title":"def sharpen_gaussian (img, alpha, kernel_size, sigma) [view source on GitHub]","text":"

Sharpen image using Gaussian blur.

Source code in albumentations/augmentations/functional.py Python
@clipped\n@preserve_channel_dim\ndef sharpen_gaussian(\n    img: np.ndarray,\n    alpha: float,\n    kernel_size: int,\n    sigma: float,\n) -> np.ndarray:\n    \"\"\"Sharpen image using Gaussian blur.\"\"\"\n    blurred = cv2.GaussianBlur(\n        img,\n        ksize=(kernel_size, kernel_size),\n        sigmaX=sigma,\n        sigmaY=sigma,\n    )\n    # Unsharp mask formula: original + alpha * (original - blurred)\n    # This is equivalent to: original * (1 + alpha) - alpha * blurred\n    return img + alpha * (img - blurred)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.shot_noise","title":"def shot_noise (img, scale, random_generator) [view source on GitHub]","text":"

Apply shot noise to the image by simulating photon counting in linear light space.

This function simulates photon shot noise, which occurs due to the quantum nature of light. The process: 1. Converts image to linear light space (removes gamma correction) 2. Scales pixel values to represent expected photon counts 3. Samples actual photon counts from Poisson distribution 4. Converts back to display space (reapplies gamma)

The simulation is performed in linear light space because photon shot noise is a physical process that occurs before gamma correction is applied by cameras/displays.

Parameters:

Name Type Description img np.ndarray

Input image in range [0, 1]. Can be single or multi-channel.

scale float

Reciprocal of the number of photons (noise intensity). - Larger values = fewer photons = more noise - Smaller values = more photons = less noise For example: - scale = 0.1 simulates ~100 photons per unit intensity - scale = 10.0 simulates ~0.1 photons per unit intensity

random_generator np.random.Generator

NumPy random generator for Poisson sampling

Returns:

Type Description Image with shot noise applied, same shape and range [0, 1] as input. The noise characteristics will follow Poisson statistics in linear space
  • Variance equals mean in linear space
  • More noise in brighter regions (but less relative noise)
  • Less noise in darker regions (but more relative noise)

Note

  • Uses gamma value of 2.2 for linear/display space conversion
  • Adds small constant (1e-6) to avoid issues with zero values
  • Clips final values to [0, 1] range
  • Operates on the image in-place for memory efficiency
  • Preserves float32 precision throughout calculations

References

  • https://en.wikipedia.org/wiki/Shot_noise
  • https://en.wikipedia.org/wiki/Gamma_correction
Source code in albumentations/augmentations/functional.py Python
@preserve_channel_dim\n@float32_io\ndef shot_noise(\n    img: np.ndarray,\n    scale: float,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply shot noise to the image by simulating photon counting in linear light space.\n\n    This function simulates photon shot noise, which occurs due to the quantum nature of light.\n    The process:\n    1. Converts image to linear light space (removes gamma correction)\n    2. Scales pixel values to represent expected photon counts\n    3. Samples actual photon counts from Poisson distribution\n    4. Converts back to display space (reapplies gamma)\n\n    The simulation is performed in linear light space because photon shot noise is a physical\n    process that occurs before gamma correction is applied by cameras/displays.\n\n    Args:\n        img: Input image in range [0, 1]. Can be single or multi-channel.\n        scale: Reciprocal of the number of photons (noise intensity).\n            - Larger values = fewer photons = more noise\n            - Smaller values = more photons = less noise\n            For example:\n            - scale = 0.1 simulates ~100 photons per unit intensity\n            - scale = 10.0 simulates ~0.1 photons per unit intensity\n        random_generator: NumPy random generator for Poisson sampling\n\n    Returns:\n        Image with shot noise applied, same shape and range [0, 1] as input.\n        The noise characteristics will follow Poisson statistics in linear space:\n        - Variance equals mean in linear space\n        - More noise in brighter regions (but less relative noise)\n        - Less noise in darker regions (but more relative noise)\n\n    Note:\n        - Uses gamma value of 2.2 for linear/display space conversion\n        - Adds small constant (1e-6) to avoid issues with zero values\n        - Clips final values to [0, 1] range\n        - Operates on the image in-place for memory efficiency\n        - Preserves float32 precision throughout calculations\n\n    References:\n        - https://en.wikipedia.org/wiki/Shot_noise\n        - https://en.wikipedia.org/wiki/Gamma_correction\n    \"\"\"\n    # Apply inverse gamma correction to work in linear space\n    img_linear = cv2.pow(img, 2.2)\n\n    # Scale image values and add small constant to avoid zero values\n    scaled_img = (img_linear + scale * 1e-6) / scale\n\n    # Generate Poisson noise\n    noisy_img = multiply_by_constant(\n        random_generator.poisson(scaled_img).astype(np.float32),\n        scale,\n        inplace=True,\n    )\n\n    # Scale back and apply gamma correction\n    return power(np.clip(noisy_img, 0, 1, out=noisy_img), 1 / 2.2)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.slic","title":"def slic (image, n_segments, compactness=10.0, max_iterations=10) [view source on GitHub]","text":"

Simple Linear Iterative Clustering (SLIC) superpixel segmentation using OpenCV and NumPy.

Parameters:

Name Type Description image np.ndarray

Input image (2D or 3D numpy array).

n_segments int

Approximate number of superpixels to generate.

compactness float

Balance between color proximity and space proximity.

max_iterations int

Maximum number of iterations for k-means.

Returns:

Type Description np.ndarray

Segmentation mask where each superpixel has a unique label.

Source code in albumentations/augmentations/functional.py Python
def slic(\n    image: np.ndarray,\n    n_segments: int,\n    compactness: float = 10.0,\n    max_iterations: int = 10,\n) -> np.ndarray:\n    \"\"\"Simple Linear Iterative Clustering (SLIC) superpixel segmentation using OpenCV and NumPy.\n\n    Args:\n        image (np.ndarray): Input image (2D or 3D numpy array).\n        n_segments (int): Approximate number of superpixels to generate.\n        compactness (float): Balance between color proximity and space proximity.\n        max_iterations (int): Maximum number of iterations for k-means.\n\n    Returns:\n        np.ndarray: Segmentation mask where each superpixel has a unique label.\n    \"\"\"\n    if image.ndim == MONO_CHANNEL_DIMENSIONS:\n        image = image[..., np.newaxis]\n\n    height, width = image.shape[:2]\n    num_pixels = height * width\n\n    # Normalize image to [0, 1] range\n    image_normalized = image.astype(np.float32) / np.max(image + 1e-6)\n\n    # Initialize cluster centers\n    grid_step = int((num_pixels / n_segments) ** 0.5)\n    x_range = np.arange(grid_step // 2, width, grid_step)\n    y_range = np.arange(grid_step // 2, height, grid_step)\n    centers = np.array(\n        [(x, y) for y in y_range for x in x_range if x < width and y < height],\n    )\n\n    # Initialize labels and distances\n    labels = -1 * np.ones((height, width), dtype=np.int32)\n    distances = np.full((height, width), np.inf)\n\n    for _ in range(max_iterations):\n        for i, center in enumerate(centers):\n            y, x = int(center[1]), int(center[0])\n\n            # Define the neighborhood\n            y_low, y_high = max(0, y - grid_step), min(height, y + grid_step + 1)\n            x_low, x_high = max(0, x - grid_step), min(width, x + grid_step + 1)\n\n            # Compute distances\n            crop = image_normalized[y_low:y_high, x_low:x_high]\n            color_diff = crop - image_normalized[y, x]\n            color_distance = np.sum(color_diff**2, axis=-1)\n\n            yy, xx = np.ogrid[y_low:y_high, x_low:x_high]\n            spatial_distance = ((yy - y) ** 2 + (xx - x) ** 2) / (grid_step**2)\n\n            distance = color_distance + compactness * spatial_distance\n\n            mask = distance < distances[y_low:y_high, x_low:x_high]\n            distances[y_low:y_high, x_low:x_high][mask] = distance[mask]\n            labels[y_low:y_high, x_low:x_high][mask] = i\n\n        # Update centers\n        for i in range(len(centers)):\n            mask = labels == i\n            if np.any(mask):\n                centers[i] = np.mean(np.argwhere(mask), axis=0)[::-1]\n\n    return labels\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.solarize","title":"def solarize (img, threshold) [view source on GitHub]","text":"

Invert all pixel values above a threshold.

Parameters:

Name Type Description img np.ndarray

The image to solarize. Can be uint8 or float32.

threshold float

Normalized threshold value in range [0, 1]. For uint8 images: pixels above threshold * 255 are inverted For float32 images: pixels above threshold are inverted

Returns:

Type Description np.ndarray

Solarized image.

Note

The threshold is normalized to [0, 1] range for both uint8 and float32 images. For uint8 images, the threshold is internally scaled by 255.

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef solarize(img: np.ndarray, threshold: float) -> np.ndarray:\n    \"\"\"Invert all pixel values above a threshold.\n\n    Args:\n        img: The image to solarize. Can be uint8 or float32.\n        threshold: Normalized threshold value in range [0, 1].\n            For uint8 images: pixels above threshold * 255 are inverted\n            For float32 images: pixels above threshold are inverted\n\n    Returns:\n        Solarized image.\n\n    Note:\n        The threshold is normalized to [0, 1] range for both uint8 and float32 images.\n        For uint8 images, the threshold is internally scaled by 255.\n    \"\"\"\n    dtype = img.dtype\n    max_val = MAX_VALUES_BY_DTYPE[dtype]\n\n    if dtype == np.uint8:\n        lut = [(max_val - i if i >= threshold * max_val else i) for i in range(int(max_val) + 1)]\n\n        prev_shape = img.shape\n        img = sz_lut(img, np.array(lut, dtype=dtype), inplace=False)\n\n        return np.expand_dims(img, -1) if len(prev_shape) != img.ndim else img\n\n    img = img.copy()\n\n    cond = img >= threshold\n    img[cond] = max_val - img[cond]\n    return img\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.square_step","title":"def square_step (pattern, y, x, step, grid_size, roughness, random_generator) [view source on GitHub]","text":"

Compute center value during square step.

Source code in albumentations/augmentations/functional.py Python
def square_step(\n    pattern: np.ndarray,\n    y: int,\n    x: int,\n    step: int,\n    grid_size: int,\n    roughness: float,\n    random_generator: np.random.Generator,\n) -> float:\n    \"\"\"Compute center value during square step.\"\"\"\n    corners = [\n        pattern[y, x],  # top-left\n        pattern[y, x + step],  # top-right\n        pattern[y + step, x],  # bottom-left\n        pattern[y + step, x + step],  # bottom-right\n    ]\n    return sum(corners) / 4.0 + random_offset(\n        step,\n        grid_size,\n        roughness,\n        random_generator,\n    )\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_average","title":"def to_gray_average (img) [view source on GitHub]","text":"

Convert an image to grayscale using the average method.

This function computes the arithmetic mean across all channels for each pixel, resulting in a grayscale representation of the image.

Key aspects of this method: 1. It treats all channels equally, regardless of their perceptual importance. 2. Works with any number of channels, making it versatile for various image types. 3. Simple and fast to compute, but may not accurately represent perceived brightness. 4. For RGB images, the formula is: Gray = (R + G + B) / 3

Note: This method may produce different results compared to weighted methods (like RGB weighted average) which account for human perception of color brightness. It may also produce unexpected results for images with alpha channels or non-color data in additional channels.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array. Can be any number of channels.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array. The output data type matches the input data type.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
def to_gray_average(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the average method.\n\n    This function computes the arithmetic mean across all channels for each pixel,\n    resulting in a grayscale representation of the image.\n\n    Key aspects of this method:\n    1. It treats all channels equally, regardless of their perceptual importance.\n    2. Works with any number of channels, making it versatile for various image types.\n    3. Simple and fast to compute, but may not accurately represent perceived brightness.\n    4. For RGB images, the formula is: Gray = (R + G + B) / 3\n\n    Note: This method may produce different results compared to weighted methods\n    (like RGB weighted average) which account for human perception of color brightness.\n    It may also produce unexpected results for images with alpha channels or\n    non-color data in additional channels.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array. Can be any number of channels.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array. The output data type\n                    matches the input data type.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    return np.mean(img, axis=-1).astype(img.dtype)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_desaturation","title":"def to_gray_desaturation (img) [view source on GitHub]","text":"

Convert an image to grayscale using the desaturation method.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef to_gray_desaturation(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the desaturation method.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    float_image = img.astype(np.float32)\n    return (np.max(float_image, axis=-1) + np.min(float_image, axis=-1)) / 2\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_from_lab","title":"def to_gray_from_lab (img) [view source on GitHub]","text":"

Convert an RGB image to grayscale using the L channel from the LAB color space.

This function converts the RGB image to the LAB color space and extracts the L channel. The LAB color space is designed to approximate human vision, where L represents lightness.

Key aspects of this method: 1. The L channel represents the lightness of each pixel, ranging from 0 (black) to 100 (white). 2. It's more perceptually uniform than RGB, meaning equal changes in L values correspond to roughly equal changes in perceived lightness. 3. The L channel is independent of the color information (A and B channels), making it suitable for grayscale conversion.

This method can be particularly useful when you want a grayscale image that closely matches human perception of lightness, potentially preserving more perceived contrast than simple RGB-based methods.

Parameters:

Name Type Description img np.ndarray

Input RGB image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array, representing the L (lightness) channel. Values are scaled to match the input image's data type range.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
@uint8_io\n@clipped\ndef to_gray_from_lab(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an RGB image to grayscale using the L channel from the LAB color space.\n\n    This function converts the RGB image to the LAB color space and extracts the L channel.\n    The LAB color space is designed to approximate human vision, where L represents lightness.\n\n    Key aspects of this method:\n    1. The L channel represents the lightness of each pixel, ranging from 0 (black) to 100 (white).\n    2. It's more perceptually uniform than RGB, meaning equal changes in L values correspond to\n       roughly equal changes in perceived lightness.\n    3. The L channel is independent of the color information (A and B channels), making it\n       suitable for grayscale conversion.\n\n    This method can be particularly useful when you want a grayscale image that closely\n    matches human perception of lightness, potentially preserving more perceived contrast\n    than simple RGB-based methods.\n\n    Args:\n        img (np.ndarray): Input RGB image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array, representing the L (lightness) channel.\n                    Values are scaled to match the input image's data type range.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    return cv2.cvtColor(img, cv2.COLOR_RGB2LAB)[..., 0]\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_max","title":"def to_gray_max (img) [view source on GitHub]","text":"

Convert an image to grayscale using the maximum channel value method.

This function takes the maximum value across all channels for each pixel, resulting in a grayscale image that preserves the brightest parts of the original image.

Key aspects of this method: 1. Works with any number of channels, making it versatile for various image types. 2. For 3-channel (e.g., RGB) images, this method is equivalent to extracting the V (Value) channel from the HSV color space. 3. Preserves the brightest parts of the image but may lose some color contrast information. 4. Simple and fast to compute.

Note: - This method tends to produce brighter grayscale images compared to other conversion methods, as it always selects the highest intensity value from the channels. - For RGB images, it may not accurately represent perceived brightness as it doesn't account for human color perception.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array. Can be any number of channels.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array. The output data type matches the input data type.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
def to_gray_max(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using the maximum channel value method.\n\n    This function takes the maximum value across all channels for each pixel,\n    resulting in a grayscale image that preserves the brightest parts of the original image.\n\n    Key aspects of this method:\n    1. Works with any number of channels, making it versatile for various image types.\n    2. For 3-channel (e.g., RGB) images, this method is equivalent to extracting the V (Value)\n       channel from the HSV color space.\n    3. Preserves the brightest parts of the image but may lose some color contrast information.\n    4. Simple and fast to compute.\n\n    Note:\n    - This method tends to produce brighter grayscale images compared to other conversion methods,\n      as it always selects the highest intensity value from the channels.\n    - For RGB images, it may not accurately represent perceived brightness as it doesn't\n      account for human color perception.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array. Can be any number of channels.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array. The output data type\n                    matches the input data type.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    return np.max(img, axis=-1)\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_pca","title":"def to_gray_pca (img) [view source on GitHub]","text":"

Convert an image to grayscale using Principal Component Analysis (PCA).

This function applies PCA to reduce a multi-channel image to a single channel, effectively creating a grayscale representation that captures the maximum variance in the color data.

Parameters:

Name Type Description img np.ndarray

Input image as a numpy array with shape (height, width, channels).

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array with shape (height, width). If input is uint8, output is uint8 in range [0, 255]. If input is float32, output is float32 in range [0, 1].

Note

This method can potentially preserve more information from the original image compared to standard weighted average methods, as it accounts for the correlations between color channels.

Image types: uint8, float32

Number of channels: any

Source code in albumentations/augmentations/functional.py Python
@clipped\ndef to_gray_pca(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an image to grayscale using Principal Component Analysis (PCA).\n\n    This function applies PCA to reduce a multi-channel image to a single channel,\n    effectively creating a grayscale representation that captures the maximum variance\n    in the color data.\n\n    Args:\n        img (np.ndarray): Input image as a numpy array with shape (height, width, channels).\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array with shape (height, width).\n                    If input is uint8, output is uint8 in range [0, 255].\n                    If input is float32, output is float32 in range [0, 1].\n\n    Note:\n        This method can potentially preserve more information from the original image\n        compared to standard weighted average methods, as it accounts for the\n        correlations between color channels.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n    \"\"\"\n    dtype = img.dtype\n    # Reshape the image to a 2D array of pixels\n    pixels = img.reshape(-1, img.shape[2])\n\n    # Perform PCA\n    pca = PCA(n_components=1)\n    pca_result = pca.fit_transform(pixels)\n\n    # Reshape back to image dimensions and scale to 0-255\n    grayscale = pca_result.reshape(img.shape[:2])\n    grayscale = normalize_per_image(grayscale, \"min_max\")\n\n    return from_float(grayscale, target_dtype=dtype) if dtype == np.uint8 else grayscale\n
"},{"location":"api_reference/augmentations/functional/#albumentations.augmentations.functional.to_gray_weighted_average","title":"def to_gray_weighted_average (img) [view source on GitHub]","text":"

Convert an RGB image to grayscale using the weighted average method.

This function uses OpenCV's cvtColor function with COLOR_RGB2GRAY conversion, which applies the following formula: Y = 0.299R + 0.587G + 0.114*B

Parameters:

Name Type Description img np.ndarray

Input RGB image as a numpy array.

Returns:

Type Description np.ndarray

Grayscale image as a 2D numpy array.

Image types: uint8, float32

Number of channels: 3

Source code in albumentations/augmentations/functional.py Python
def to_gray_weighted_average(img: np.ndarray) -> np.ndarray:\n    \"\"\"Convert an RGB image to grayscale using the weighted average method.\n\n    This function uses OpenCV's cvtColor function with COLOR_RGB2GRAY conversion,\n    which applies the following formula:\n    Y = 0.299*R + 0.587*G + 0.114*B\n\n    Args:\n        img (np.ndarray): Input RGB image as a numpy array.\n\n    Returns:\n        np.ndarray: Grayscale image as a 2D numpy array.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n    \"\"\"\n    return cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)\n
"},{"location":"api_reference/augmentations/geometric/","title":"Geometric augmentations (augmentations.geometric)","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional","title":"functional","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.adjust_padding_by_position","title":"def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py Python
def adjust_padding_by_position(\n    h_top: int,\n    h_bottom: int,\n    w_left: int,\n    w_right: int,\n    position: PositionType,\n    py_random: np.random.RandomState,\n) -> tuple[int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position.\"\"\"\n    if position == \"center\":\n        return h_top, h_bottom, w_left, w_right\n\n    if position == \"top_left\":\n        return 0, h_top + h_bottom, 0, w_left + w_right\n\n    if position == \"top_right\":\n        return 0, h_top + h_bottom, w_left + w_right, 0\n\n    if position == \"bottom_left\":\n        return h_top + h_bottom, 0, 0, w_left + w_right\n\n    if position == \"bottom_right\":\n        return h_top + h_bottom, 0, w_left + w_right, 0\n\n    if position == \"random\":\n        h_pad = h_top + h_bottom\n        w_pad = w_left + w_right\n        h_top = py_random.randint(0, h_pad)\n        h_bottom = h_pad - h_top\n        w_left = py_random.randint(0, w_pad)\n        w_right = w_pad - w_left\n        return h_top, h_bottom, w_left, w_right\n\n    raise ValueError(f\"Unknown position: {position}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.almost_equal_intervals","title":"def almost_equal_intervals (n, parts) [view source on GitHub]","text":"

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)\narray([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n>>> almost_equal_intervals(16, 4)\narray([4, 4, 4, 4])  # Splits 16 into four equal parts\n
Source code in albumentations/augmentations/geometric/functional.py Python
def almost_equal_intervals(n: int, parts: int) -> np.ndarray:\n    \"\"\"Generates an array of nearly equal integer intervals that sum up to `n`.\n\n    This function divides the number `n` into `parts` nearly equal parts. It ensures that\n    the sum of all parts equals `n`, and the difference between any two parts is at most one.\n    This is useful for distributing a total amount into nearly equal discrete parts.\n\n    Args:\n        n (int): The total value to be split.\n        parts (int): The number of parts to split into.\n\n    Returns:\n        np.ndarray: An array of integers where each integer represents the size of a part.\n\n    Example:\n        >>> almost_equal_intervals(20, 3)\n        array([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n        >>> almost_equal_intervals(16, 4)\n        array([4, 4, 4, 4])  # Splits 16 into four equal parts\n    \"\"\"\n    part_size, remainder = divmod(n, parts)\n    # Create an array with the base part size and adjust the first `remainder` parts by adding 1\n    return np.array(\n        [part_size + 1 if i < remainder else part_size for i in range(parts)],\n    )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.apply_affine_to_points","title":"def apply_affine_to_points (points, matrix) [view source on GitHub]","text":"

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"points\")\ndef apply_affine_to_points(points: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply affine transformation to a set of points.\n\n    This function handles potential division by zero by replacing zero values\n    in the homogeneous coordinate with a small epsilon value.\n\n    Args:\n        points (np.ndarray): Array of points with shape (N, 2).\n        matrix (np.ndarray): 3x3 affine transformation matrix.\n\n    Returns:\n        np.ndarray: Transformed points with shape (N, 2).\n    \"\"\"\n    homogeneous_points = np.column_stack([points, np.ones(points.shape[0])])\n    transformed_points = homogeneous_points @ matrix.T\n\n    # Handle potential division by zero\n    epsilon = np.finfo(transformed_points.dtype).eps\n    transformed_points[:, 2] = np.where(\n        np.abs(transformed_points[:, 2]) < epsilon,\n        np.sign(transformed_points[:, 2]) * epsilon,\n        transformed_points[:, 2],\n    )\n\n    return transformed_points[:, :2] / transformed_points[:, 2:]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine","title":"def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine(\n    bboxes: np.ndarray,\n    matrix: np.ndarray,\n    rotate_method: Literal[\"largest_box\", \"ellipse\"],\n    image_shape: tuple[int, int],\n    border_mode: int,\n    output_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes.\n\n    For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function:\n    1. Calculates necessary padding to avoid information loss\n    2. Applies padding to the bounding boxes\n    3. Adjusts the transformation matrix to account for padding\n    4. Applies the affine transformation\n    5. Validates the transformed bounding boxes\n\n    For other border modes, it directly applies the affine transformation without padding.\n\n    Args:\n        bboxes (np.ndarray): Input bounding boxes\n        matrix (np.ndarray): Affine transformation matrix\n        rotate_method (str): Method for rotating bounding boxes ('largest_box' or 'ellipse')\n        image_shape (Sequence[int]): Shape of the input image\n        border_mode (int): OpenCV border mode\n        output_shape (Sequence[int]): Shape of the output image\n\n    Returns:\n        np.ndarray: Transformed and normalized bounding boxes\n    \"\"\"\n    if is_identity_matrix(matrix):\n        return bboxes\n\n    bboxes = denormalize_bboxes(bboxes, image_shape)\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        bboxes = generate_reflected_bboxes(\n            bboxes,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Apply affine transform\n    if rotate_method == \"largest_box\":\n        transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n    elif rotate_method == \"ellipse\":\n        transformed_bboxes = bboxes_affine_ellipse(bboxes, matrix)\n    else:\n        raise ValueError(f\"Method {rotate_method} is not a valid rotation method.\")\n\n    # Validate and normalize bboxes\n    validated_bboxes = validate_bboxes(transformed_bboxes, output_shape)\n\n    return normalize_bboxes(validated_bboxes, output_shape)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine_ellipse","title":"def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_ellipse(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes using an ellipse approximation method.\n\n    This function transforms bounding boxes by approximating each box with an ellipse,\n    transforming points along the ellipse's circumference, and then computing the\n    new bounding box that encloses the transformed ellipse.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The ellipse approximation method can provide a tighter bounding box compared to the\n          largest box method, especially for rotations.\n        - 360 points are used to approximate each ellipse, which provides a good balance between\n          accuracy and computational efficiency.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method may be more suitable for objects that are roughly elliptical in shape.\n    \"\"\"\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n    bbox_width = (x_max - x_min) / 2\n    bbox_height = (y_max - y_min) / 2\n    center_x = x_min + bbox_width\n    center_y = y_min + bbox_height\n\n    angles = np.arange(0, 360, dtype=np.float32)\n    cos_angles = np.cos(np.radians(angles))\n    sin_angles = np.sin(np.radians(angles))\n\n    # Generate points for all ellipses at once\n    x = bbox_width[:, np.newaxis] * sin_angles + center_x[:, np.newaxis]\n    y = bbox_height[:, np.newaxis] * cos_angles + center_y[:, np.newaxis]\n    points = np.stack([x, y], axis=-1).reshape(-1, 2)\n\n    # Transform all points at once using the helper function\n    transformed_points = apply_affine_to_points(points, matrix)\n\n    transformed_points = transformed_points.reshape(len(bboxes), -1, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_points[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_points[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_points[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_points[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_affine_largest_box","title":"def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called \"largest box\" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n>>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n>>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n>>> print(transformed_bboxes)\n[[ 25.  25.  45.  45.   1.]\n [ 65.  65.  85.  85.   2.]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_largest_box(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes and return the largest enclosing boxes.\n\n    This function transforms each corner of every bounding box using the given affine transformation\n    matrix, then computes the new bounding boxes that fully enclose the transformed corners.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The resulting bounding boxes are the smallest axis-aligned boxes that completely\n          enclose the transformed original boxes. They may be larger than the minimal possible\n          bounding box if the original box becomes rotated.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method is called \"largest box\" because it returns the largest axis-aligned box\n          that encloses all corners of the transformed bounding box.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n        >>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n        >>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n        >>> print(transformed_bboxes)\n        [[ 25.  25.  45.  45.   1.]\n         [ 65.  65.  85.  85.   2.]]\n    \"\"\"\n    # Extract corners of all bboxes\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    corners = (\n        np.array([[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]]).transpose(2, 0, 1).reshape(-1, 2)\n    )\n\n    # Transform all corners at once\n    transformed_corners = apply_affine_to_points(corners, matrix).reshape(-1, 4, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_corners[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_corners[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_corners[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_corners[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_d4","title":"def bboxes_d4 (bboxes, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_d4(\n    bboxes: np.ndarray,\n    group_member: D4Type,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a bounding box.\n\n    The function transforms a bounding box according to the specified group member from the `D_4` group.\n    These transformations include rotations and reflections, specified to work on an image's bounding box given\n    its dimensions.\n\n    Parameters:\n    -  bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n    - group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.\n\n    Returns:\n    - BoxInternalType: The transformed bounding box.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Applying a 90-degree rotation:\n      `bbox_d4((10, 20, 110, 120), 'r90')`\n      This would rotate the bounding box 90 degrees within a 100x100 image.\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: bboxes_rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: bboxes_rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: bboxes_rot90(x, 3),  # Rotate 270 degrees\n        \"v\": lambda x: bboxes_vflip(x),  # Vertical flip\n        \"hvt\": lambda x: bboxes_transpose(\n            bboxes_rot90(x, 2),\n        ),  # Reflect over anti-diagonal\n        \"h\": lambda x: bboxes_hflip(x),  # Horizontal flip\n        \"t\": lambda x: bboxes_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](bboxes)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_grid_shuffle","title":"def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n>>> tiles = np.array([\n...     [0, 0, 50, 50],    # top-left tile\n...     [0, 50, 50, 100],  # top-right tile\n...     [50, 0, 100, 50],  # bottom-left tile\n...     [50, 50, 100, 100] # bottom-right tile\n... ])\n>>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n>>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n>>> # Result may contain multiple boxes if the original box was split\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_grid_shuffle(\n    bboxes: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int],\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Apply grid shuffle transformation to bounding boxes.\n\n    This function transforms bounding boxes according to a grid shuffle operation. It handles cases\n    where bounding boxes may be split into multiple components after shuffling and applies\n    filtering based on minimum area and visibility requirements.\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4+) where N is the number of boxes.\n               Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents\n               optional additional fields (e.g., class_id, score).\n        tiles: Array of tile coordinates with shape (M, 4) where M is the number of tiles.\n               Each tile is in format [start_y, start_x, end_y, end_x].\n        mapping: List of indices defining how tiles should be rearranged. Each index i in the list\n                contains the index of the tile that should be moved to position i.\n        image_shape: Shape of the image as (height, width).\n        min_area: Minimum area threshold in pixels. If a component's area after shuffling is\n                 smaller than this value, it will be filtered out. If None, no area filtering\n                 is applied.\n        min_visibility: Minimum visibility ratio threshold in range [0, 1]. Calculated as\n                       (component_area / original_area). If a component's visibility is lower\n                       than this value, it will be filtered out. If None, no visibility\n                       filtering is applied.\n\n    Returns:\n        np.ndarray: Array of transformed bounding boxes with shape (K, 4+) where K is the\n                   number of valid components after shuffling and filtering. The format of\n                   each box matches the input format, preserving any additional fields.\n                   If no valid components remain after filtering, returns an empty array\n                   with shape (0, C) where C matches the input column count.\n\n    Note:\n        - The function converts bboxes to masks before applying the transformation to handle\n          cases where boxes may be split into multiple components.\n        - After shuffling, each component is validated against min_area and min_visibility\n          requirements independently.\n        - Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and\n          copied to all components derived from the same original bbox.\n        - Empty input arrays are handled gracefully and return empty arrays of the\n          appropriate shape.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n        >>> tiles = np.array([\n        ...     [0, 0, 50, 50],    # top-left tile\n        ...     [0, 50, 50, 100],  # top-right tile\n        ...     [50, 0, 100, 50],  # bottom-left tile\n        ...     [50, 50, 100, 100] # bottom-right tile\n        ... ])\n        >>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n        >>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n        >>> # Result may contain multiple boxes if the original box was split\n    \"\"\"\n    # Convert bboxes to masks\n    masks = masks_from_bboxes(bboxes, image_shape)\n\n    # Apply grid shuffle to each mask and handle split components\n    all_component_masks = []\n    extra_bbox_data = []  # Store additional bbox data for each component\n\n    for idx, mask in enumerate(masks):\n        original_area = np.sum(mask)  # Get original mask area\n\n        # Shuffle the mask\n        shuffled_mask = swap_tiles_on_image(mask, tiles, mapping)\n\n        # Find connected components\n        num_components, components = cv2.connectedComponents(\n            shuffled_mask.astype(np.uint8),\n        )\n\n        # For each component, create a separate binary mask\n        for comp_idx in range(1, num_components):  # Skip background (0)\n            component_mask = (components == comp_idx).astype(np.uint8)\n\n            # Calculate area and visibility ratio\n            component_area = np.sum(component_mask)\n            # Check if component meets minimum requirements\n            if is_valid_component(\n                component_area,\n                original_area,\n                min_area,\n                min_visibility,\n            ):\n                all_component_masks.append(component_mask)\n                # Append additional bbox data for this component\n                if bboxes.shape[1] > NUM_BBOXES_COLUMNS_IN_ALBUMENTATIONS:\n                    extra_bbox_data.append(bboxes[idx, 4:])\n\n    # Convert all component masks to bboxes\n    if all_component_masks:\n        all_component_masks = np.array(all_component_masks)\n        shuffled_bboxes = bboxes_from_masks(all_component_masks)\n\n        # Add back additional bbox data if present\n        if extra_bbox_data:\n            extra_bbox_data = np.array(extra_bbox_data)\n            return np.column_stack([shuffled_bboxes, extra_bbox_data])\n    else:\n        # Handle case where no valid components were found\n        return np.zeros((0, bboxes.shape[1]), dtype=bboxes.dtype)\n\n    return shuffled_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_hflip","title":"def bboxes_hflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_hflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally around the y-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of horizontally flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 0] = 1 - bboxes[:, 2]  # new x_min = 1 - x_max\n    flipped_bboxes[:, 2] = 1 - bboxes[:, 0]  # new x_max = 1 - x_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_rot90","title":"def bboxes_rot90 (bboxes, factor) [view source on GitHub]","text":"

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor Literal[0, 1, 2, 3]

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_rot90(bboxes: np.ndarray, factor: Literal[0, 1, 2, 3]) -> np.ndarray:\n    \"\"\"Rotates bounding boxes by 90 degrees CCW (see np.rot90)\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n        factor: Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.\n\n    Returns:\n        np.ndarray: A numpy array of rotated bounding boxes with the same shape as input.\n    \"\"\"\n    if factor == 0:\n        return bboxes\n\n    rotated_bboxes = bboxes.copy()\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    if factor == 1:\n        rotated_bboxes[:, 0] = y_min\n        rotated_bboxes[:, 1] = 1 - x_max\n        rotated_bboxes[:, 2] = y_max\n        rotated_bboxes[:, 3] = 1 - x_min\n    elif factor == ROT90_180_FACTOR:\n        rotated_bboxes[:, 0] = 1 - x_max\n        rotated_bboxes[:, 1] = 1 - y_max\n        rotated_bboxes[:, 2] = 1 - x_min\n        rotated_bboxes[:, 3] = 1 - y_min\n    elif factor == ROT90_270_FACTOR:\n        rotated_bboxes[:, 0] = 1 - y_max\n        rotated_bboxes[:, 1] = x_min\n        rotated_bboxes[:, 2] = 1 - y_min\n        rotated_bboxes[:, 3] = x_max\n\n    return rotated_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_transpose","title":"def bboxes_transpose (bboxes) [view source on GitHub]","text":"

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_transpose(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Transpose bounding boxes by swapping x and y coordinates.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of transposed bounding boxes with the same shape as input.\n    \"\"\"\n    transposed_bboxes = bboxes.copy()\n    transposed_bboxes[:, [0, 1, 2, 3]] = bboxes[:, [1, 0, 3, 2]]\n\n    return transposed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.bboxes_vflip","title":"def bboxes_vflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_vflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes vertically around the x-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of vertically flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 1] = 1 - bboxes[:, 3]  # new y_min = 1 - y_max\n    flipped_bboxes[:, 3] = 1 - bboxes[:, 1]  # new y_max = 1 - y_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.calculate_affine_transform_padding","title":"def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]","text":"

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py Python
def calculate_affine_transform_padding(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate the necessary padding for an affine transformation to avoid empty spaces.\"\"\"\n    height, width = image_shape[:2]\n\n    # Check for identity transform\n    if is_identity_matrix(matrix):\n        return (0, 0, 0, 0)\n\n    # Original corners\n    corners = np.array([[0, 0], [width, 0], [width, height], [0, height]])\n\n    # Transform corners\n    transformed_corners = apply_affine_to_points(corners, matrix)\n\n    # Ensure transformed_corners is 2D\n    transformed_corners = transformed_corners.reshape(-1, 2)\n\n    # Find box that includes both original and transformed corners\n    all_corners = np.vstack((corners, transformed_corners))\n    min_x, min_y = all_corners.min(axis=0)\n    max_x, max_y = all_corners.max(axis=0)\n\n    # Compute the inverse transform\n    inverse_matrix = np.linalg.inv(matrix)\n\n    # Apply inverse transform to all corners of the bounding box\n    bbox_corners = np.array(\n        [[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]],\n    )\n    inverse_corners = apply_affine_to_points(bbox_corners, inverse_matrix).reshape(\n        -1,\n        2,\n    )\n\n    min_x, min_y = inverse_corners.min(axis=0)\n    max_x, max_y = inverse_corners.max(axis=0)\n\n    pad_left = max(0, math.ceil(0 - min_x))\n    pad_right = max(0, math.ceil(max_x - width))\n    pad_top = max(0, math.ceil(0 - min_y))\n    pad_bottom = max(0, math.ceil(max_y - height))\n\n    return pad_left, pad_right, pad_top, pad_bottom\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.center","title":"def center (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates if image. Used by images, masks and keypoints.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2 - 0.5, height / 2 - 0.5\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.center_bbox","title":"def center_bbox (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates for of image for bounding boxes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2, height / 2\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.compute_tps_weights","title":"def compute_tps_weights (src_points, dst_points) [view source on GitHub]","text":"

Compute Thin Plate Spline weights.

Parameters:

Name Type Description src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_tps_weights(\n    src_points: np.ndarray,\n    dst_points: np.ndarray,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute Thin Plate Spline weights.\n\n    Args:\n        src_points: Source control points with shape (num_points, 2)\n        dst_points: Destination control points with shape (num_points, 2)\n\n    Returns:\n        tuple of:\n        - nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)\n        - affine_weights: Weights for affine transformation (3, 2)\n            [constant term, x scale/shear, y scale/shear]\n\n    Note:\n        The TPS interpolation is decomposed into:\n        1. Nonlinear part (controlled by kernel weights)\n        2. Affine part (global scaling, rotation, translation)\n    \"\"\"\n    num_points = src_points.shape[0]\n\n    # Compute pairwise distances\n    distances = np.linalg.norm(src_points[:, None] - src_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    # Add small epsilon to avoid log(0)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Construct affine terms matrix [1, x, y]\n    affine_terms = np.ones((num_points, 3))\n    affine_terms[:, 1:] = src_points\n\n    # Build system matrix\n    system_matrix = np.zeros((num_points + 3, num_points + 3))\n    system_matrix[:num_points, :num_points] = kernel_matrix\n    system_matrix[:num_points, num_points:] = affine_terms\n    system_matrix[num_points:, :num_points] = affine_terms.T\n\n    # Right-hand side of the system\n    target_coords = np.zeros((num_points + 3, 2))\n    target_coords[:num_points] = dst_points\n\n    # Solve the system for both x and y coordinates\n    all_weights = np.linalg.solve(system_matrix, target_coords)\n\n    # Split weights into nonlinear and affine components\n    nonlinear_weights = all_weights[:num_points]\n    affine_weights = all_weights[num_points:]\n\n    return nonlinear_weights, affine_weights\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.compute_transformed_image_bounds","title":"def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]","text":"

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_transformed_image_bounds(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute the bounds of an image after applying an affine transformation.\n\n    Args:\n        matrix (np.ndarray): The 3x3 affine transformation matrix.\n        image_shape (Tuple[int, int]): The shape of the image as (height, width).\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing:\n            - min_coords: An array with the minimum x and y coordinates.\n            - max_coords: An array with the maximum x and y coordinates.\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Define the corners of the image\n    corners = np.array([[0, 0, 1], [width, 0, 1], [width, height, 1], [0, height, 1]])\n\n    # Transform the corners\n    transformed_corners = corners @ matrix.T\n    transformed_corners = transformed_corners[:, :2] / transformed_corners[:, 2:]\n\n    # Calculate the bounding box of the transformed corners\n    min_coords = np.floor(transformed_corners.min(axis=0)).astype(int)\n    max_coords = np.ceil(transformed_corners.max(axis=0)).astype(int)\n\n    return min_coords, max_coords\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_affine_transformation_matrix","title":"def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]","text":"

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_affine_transformation_matrix(\n    translate: XYInt,\n    shear: XYFloat,\n    scale: XYFloat,\n    rotate: float,\n    shift: tuple[float, float],\n) -> np.ndarray:\n    \"\"\"Create an affine transformation matrix combining translation, shear, scale, and rotation.\n\n    Args:\n        translate (dict[str, float]): Translation in x and y directions.\n        shear (dict[str, float]): Shear in x and y directions (in degrees).\n        scale (dict[str, float]): Scale factors for x and y directions.\n        rotate (float): Rotation angle in degrees.\n        shift (tuple[float, float]): Shift to apply before and after transformations.\n\n    Returns:\n        np.ndarray: The resulting 3x3 affine transformation matrix.\n    \"\"\"\n    # Convert angles to radians\n    rotate_rad = np.deg2rad(rotate % 360)\n\n    shear_x_rad = np.deg2rad(shear[\"x\"])\n    shear_y_rad = np.deg2rad(shear[\"y\"])\n\n    # Create individual transformation matrices\n    # 1. Shift to top-left\n    m_shift_topleft = np.array([[1, 0, -shift[0]], [0, 1, -shift[1]], [0, 0, 1]])\n\n    # 2. Scale\n    m_scale = np.array([[scale[\"x\"], 0, 0], [0, scale[\"y\"], 0], [0, 0, 1]])\n\n    # 3. Rotation\n    m_rotate = np.array(\n        [\n            [np.cos(rotate_rad), np.sin(rotate_rad), 0],\n            [-np.sin(rotate_rad), np.cos(rotate_rad), 0],\n            [0, 0, 1],\n        ],\n    )\n\n    # 4. Shear\n    m_shear = np.array(\n        [[1, np.tan(shear_x_rad), 0], [np.tan(shear_y_rad), 1, 0], [0, 0, 1]],\n    )\n\n    # 5. Translation\n    m_translate = np.array([[1, 0, translate[\"x\"]], [0, 1, translate[\"y\"]], [0, 0, 1]])\n\n    # 6. Shift back to center\n    m_shift_center = np.array([[1, 0, shift[0]], [0, 1, shift[1]], [0, 0, 1]])\n\n    # Combine all transformations\n    # The order is important: transformations are applied from right to left\n    m = m_shift_center @ m_translate @ m_shear @ m_rotate @ m_scale @ m_shift_topleft\n\n    # Ensure the last row is exactly [0, 0, 1]\n    m[2] = [0, 0, 1]\n\n    return m\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_piecewise_affine_maps","title":"def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]","text":"

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_piecewise_affine_maps(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    scale: float,\n    absolute_scale: bool,\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray | None, np.ndarray | None]:\n    \"\"\"Create maps for piecewise affine transformation using OpenCV's remap function.\"\"\"\n    height, width = image_shape[:2]\n    nb_rows, nb_cols = grid\n\n    # Input validation\n    if height <= 0 or width <= 0 or nb_rows <= 0 or nb_cols <= 0:\n        raise ValueError(\"Dimensions must be positive\")\n    if scale <= 0:\n        return None, None\n\n    # Create source points grid\n    y = np.linspace(0, height - 1, nb_rows, dtype=np.float32)\n    x = np.linspace(0, width - 1, nb_cols, dtype=np.float32)\n    xx_src, yy_src = np.meshgrid(x, y)\n\n    # Initialize destination maps at full resolution\n    map_x = np.zeros((height, width), dtype=np.float32)\n    map_y = np.zeros((height, width), dtype=np.float32)\n\n    # Generate jitter for control points\n    jitter_scale = scale / 3 if absolute_scale else scale * min(width, height) / 3\n\n    jitter = random_generator.normal(0, jitter_scale, (nb_rows, nb_cols, 2)).astype(\n        np.float32,\n    )\n\n    # Create control points with jitter\n    control_points = np.zeros((nb_rows * nb_cols, 4), dtype=np.float32)\n    for i in range(nb_rows):\n        for j in range(nb_cols):\n            idx = i * nb_cols + j\n            # Source points\n            control_points[idx, 0] = xx_src[i, j]\n            control_points[idx, 1] = yy_src[i, j]\n            # Destination points with jitter\n            control_points[idx, 2] = np.clip(\n                xx_src[i, j] + jitter[i, j, 1],\n                0,\n                width - 1,\n            )\n            control_points[idx, 3] = np.clip(\n                yy_src[i, j] + jitter[i, j, 0],\n                0,\n                height - 1,\n            )\n\n    # Create full resolution maps\n    for i in range(height):\n        for j in range(width):\n            # Find nearest control points and interpolate\n            dx = j - control_points[:, 0]\n            dy = i - control_points[:, 1]\n            dist = dx * dx + dy * dy\n            weights = 1 / (dist + 1e-8)\n            weights = weights / np.sum(weights)\n\n            map_x[i, j] = np.sum(weights * control_points[:, 2])\n            map_y[i, j] = np.sum(weights * control_points[:, 3])\n\n    # Ensure output is within bounds\n    map_x = np.clip(map_x, 0, width - 1, out=map_x)\n    map_y = np.clip(map_y, 0, height - 1, out=map_y)\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.create_shape_groups","title":"def create_shape_groups (tiles) [view source on GitHub]","text":"

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:\n    \"\"\"Groups tiles by their shape and stores the indices for each shape.\"\"\"\n    shape_groups = defaultdict(list)\n    for index, (start_y, start_x, end_y, end_x) in enumerate(tiles):\n        shape = (end_y - start_y, end_x - start_x)\n        shape_groups[shape].append(index)\n    return shape_groups\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.d4","title":"def d4 (img, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to an image array.\n\n    This function manipulates an image using transformations such as rotations and flips,\n    corresponding to the `D_4` dihedral group symmetry operations.\n    Each transformation is identified by a unique group member code.\n\n    Parameters:\n    - img (np.ndarray): The input image array to transform.\n    - group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:\n      - 'e': Identity (no transformation).\n      - 'r90': Rotate 90 degrees counterclockwise.\n      - 'r180': Rotate 180 degrees.\n      - 'r270': Rotate 270 degrees counterclockwise.\n      - 'v': Vertical flip.\n      - 'hvt': Transpose over second diagonal\n      - 'h': Horizontal flip.\n      - 't': Transpose (reflect over the main diagonal).\n\n    Returns:\n    - np.ndarray: The transformed image array.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Rotating an image by 90 degrees:\n      `transformed_image = d4(original_image, 'r90')`\n    - Applying a horizontal flip to an image:\n      `transformed_image = d4(original_image, 'h')`\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: rot90(x, 3),  # Rotate 270 degrees\n        \"v\": vflip,  # Vertical flip\n        \"hvt\": lambda x: transpose(rot90(x, 2)),  # Reflect over anti-diagonal\n        \"h\": hflip,  # Horizontal flip\n        \"t\": transpose,  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](img)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.distort_image","title":"def distort_image (image, generated_mesh, interpolation) [view source on GitHub]","text":"

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n>>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n>>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n>>> distorted.shape\n(100, 100, 3)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@preserve_channel_dim\ndef distort_image(\n    image: np.ndarray,\n    generated_mesh: np.ndarray,\n    interpolation: int,\n) -> np.ndarray:\n    \"\"\"Apply perspective distortion to an image based on a generated mesh.\n\n    This function applies a perspective transformation to each cell of the image defined by the\n    generated mesh. The distortion is applied using OpenCV's perspective transformation and\n    blending techniques.\n\n    Args:\n        image (np.ndarray): The input image to be distorted. Can be a 2D grayscale image or a\n                            3D color image.\n        generated_mesh (np.ndarray): A 2D array where each row represents a quadrilateral cell\n                                    as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4].\n                                    The first four values define the source rectangle, and the last eight values\n                                    define the destination quadrilateral.\n        interpolation (int): Interpolation method to be used in the perspective transformation.\n                             Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).\n\n    Returns:\n        np.ndarray: The distorted image with the same shape and dtype as the input image.\n\n    Note:\n        - The function preserves the channel dimension of the input image.\n        - Each cell of the generated mesh is transformed independently and then blended into the output image.\n        - The distortion is applied using perspective transformation, which allows for more complex\n          distortions compared to affine transformations.\n\n    Example:\n        >>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n        >>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n        >>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n        >>> distorted.shape\n        (100, 100, 3)\n    \"\"\"\n    distorted_image = np.zeros_like(image)\n\n    for mesh in generated_mesh:\n        # Extract source rectangle and destination quadrilateral\n        x1, y1, x2, y2 = mesh[:4]  # Source rectangle\n        dst_quad = mesh[4:].reshape(4, 2)  # Destination quadrilateral\n\n        # Convert source rectangle to quadrilateral\n        src_quad = np.array(\n            [\n                [x1, y1],  # Top-left\n                [x2, y1],  # Top-right\n                [x2, y2],  # Bottom-right\n                [x1, y2],  # Bottom-left\n            ],\n            dtype=np.float32,\n        )\n\n        # Calculate Perspective transformation matrix\n        perspective_mat = cv2.getPerspectiveTransform(src_quad, dst_quad)\n\n        # Apply Perspective transformation\n        warped = cv2.warpPerspective(\n            image,\n            perspective_mat,\n            (image.shape[1], image.shape[0]),\n            flags=interpolation,\n        )\n\n        # Create mask for the transformed region\n        mask = np.zeros(image.shape[:2], dtype=np.uint8)\n        cv2.fillConvexPoly(mask, np.int32(dst_quad), 255)\n\n        # Copy only the warped quadrilateral area to the output image\n        distorted_image = cv2.copyTo(warped, mask, distorted_image)\n\n    return distorted_image\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.find_keypoint","title":"def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]","text":"

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py Python
def find_keypoint(\n    position: tuple[int, int],\n    distance_map: np.ndarray,\n    threshold: float | None,\n    inverted: bool,\n) -> tuple[float, float] | None:\n    \"\"\"Determine if a valid keypoint can be found at the given position.\"\"\"\n    y, x = position\n    value = distance_map[y, x]\n    if not inverted and threshold is not None and value >= threshold:\n        return None\n    if inverted and threshold is not None and value <= threshold:\n        return None\n    return float(x), float(y)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.flip_bboxes","title":"def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]","text":"

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef flip_bboxes(\n    bboxes: np.ndarray,\n    flip_horizontal: bool = False,\n    flip_vertical: bool = False,\n    image_shape: tuple[int, int] = (0, 0),\n) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally and/or vertically.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where each row is\n            [x_min, y_min, x_max, y_max, ...].\n        flip_horizontal (bool): Whether to flip horizontally.\n        flip_vertical (bool): Whether to flip vertically.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Flipped bounding boxes.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    flipped_bboxes = bboxes.copy()\n    if flip_horizontal:\n        flipped_bboxes[:, [0, 2]] = cols - flipped_bboxes[:, [2, 0]]\n    if flip_vertical:\n        flipped_bboxes[:, [1, 3]] = rows - flipped_bboxes[:, [3, 1]]\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.from_distance_maps","title":"def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]","text":"

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n>>> inverted = True\n>>> if_not_found_coords = [0, 0]\n>>> threshold = 0.5\n>>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n>>> print(keypoints.shape)\n(3, 2)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def from_distance_maps(\n    distance_maps: np.ndarray,\n    inverted: bool,\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None = None,\n    threshold: float | None = None,\n) -> np.ndarray:\n    \"\"\"Convert distance maps back to keypoints coordinates.\n\n    This function is the inverse of `to_distance_maps`. It takes distance maps generated for a set of keypoints\n    and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps,\n    and can handle cases where keypoints are not found or fall outside a specified threshold.\n\n    Args:\n        distance_maps (np.ndarray): A 3D numpy array of shape (height, width, nb_keypoints) containing\n            distance maps for each keypoint. Each channel represents the distance map for one keypoint.\n        inverted (bool): If True, treats the distance maps as inverted (where higher values indicate\n            closer proximity to keypoints). If False, treats them as regular distance maps (where lower\n            values indicate closer proximity).\n        if_not_found_coords (Sequence[int] | dict[str, Any] | None, optional): Coordinates to use for\n            keypoints that are not found or fall outside the threshold. Can be:\n            - None: Drop keypoints that are not found.\n            - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints.\n            - Dict with 'x' and 'y' keys: Use these values for not found keypoints.\n            Defaults to None.\n        threshold (float | None, optional): A threshold value to determine valid keypoints. For inverted\n            maps, values >= threshold are considered valid. For regular maps, values <= threshold are\n            considered valid. If None, all keypoints are considered valid. Defaults to None.\n\n    Returns:\n        np.ndarray: A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates\n        of the reconstructed keypoints. If `drop_if_not_found` is True (derived from if_not_found_coords),\n        the output may have fewer rows than input keypoints.\n\n    Raises:\n        ValueError: If the input `distance_maps` is not a 3D array.\n\n    Notes:\n        - The function uses vectorized operations for improved performance, especially with large numbers of keypoints.\n        - When `threshold` is None, all keypoints are considered valid, and `if_not_found_coords` is not used.\n        - The function assumes that the input distance maps are properly normalized and scaled according to the\n          original image dimensions.\n\n    Example:\n        >>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n        >>> inverted = True\n        >>> if_not_found_coords = [0, 0]\n        >>> threshold = 0.5\n        >>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n        >>> print(keypoints.shape)\n        (3, 2)\n    \"\"\"\n    if distance_maps.ndim != NUM_MULTI_CHANNEL_DIMENSIONS:\n        msg = f\"Expected three-dimensional input, got {distance_maps.ndim} dimensions and shape {distance_maps.shape}.\"\n        raise ValueError(msg)\n    height, width, nb_keypoints = distance_maps.shape\n\n    drop_if_not_found, if_not_found_x, if_not_found_y = validate_if_not_found_coords(\n        if_not_found_coords,\n    )\n\n    # Find the indices of max/min values for all keypoints at once\n    if inverted:\n        hitidx_flat = np.argmax(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n    else:\n        hitidx_flat = np.argmin(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n\n    # Convert flat indices to 2D coordinates\n    hitidx_y, hitidx_x = np.unravel_index(hitidx_flat, (height, width))\n\n    # Create keypoints array\n    keypoints = np.column_stack((hitidx_x, hitidx_y)).astype(float)\n\n    if threshold is not None:\n        # Check threshold condition\n        if inverted:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] >= threshold\n        else:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] <= threshold\n\n        if not drop_if_not_found:\n            # Replace invalid keypoints with if_not_found_coords\n            keypoints[~valid_mask] = [if_not_found_x, if_not_found_y]\n        else:\n            # Keep only valid keypoints\n            return keypoints[valid_mask]\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_displacement_fields","title":"def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]","text":"

Generate displacement fields for elastic transform.

Parameters:

Name Type Description image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use (\"gaussian\" or \"uniform\")

Returns:

Type Description tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_displacement_fields(\n    image_shape: tuple[int, int],\n    alpha: float,\n    sigma: float,\n    same_dxdy: bool,\n    kernel_size: tuple[int, int],\n    random_generator: np.random.Generator,\n    noise_distribution: Literal[\"gaussian\", \"uniform\"],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate displacement fields for elastic transform.\n\n    Args:\n        image_shape: Shape of the image (height, width)\n        alpha: Scaling factor for displacement\n        sigma: Standard deviation for Gaussian blur\n        same_dxdy: Whether to use same displacement field for both directions\n        kernel_size: Size of Gaussian blur kernel\n        random_generator: NumPy random number generator\n        noise_distribution: Type of noise distribution to use (\"gaussian\" or \"uniform\")\n\n    Returns:\n        tuple: (dx, dy) displacement fields\n    \"\"\"\n\n    def generate_noise_field() -> np.ndarray:\n        # Generate noise based on distribution type\n        if noise_distribution == \"gaussian\":\n            field = random_generator.standard_normal(size=image_shape[:2])\n        else:  # uniform\n            field = random_generator.uniform(low=-1, high=1, size=image_shape[:2])\n\n        # Common operations for both distributions\n        field = field.astype(np.float32)\n        cv2.GaussianBlur(field, kernel_size, sigma, dst=field)\n        return field * alpha\n\n    # Generate first displacement field\n    dx = generate_noise_field()\n\n    # Generate or copy second displacement field\n    dy = dx if same_dxdy else generate_noise_field()\n\n    return dx, dy\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_distorted_grid_polygons","title":"def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]","text":"

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n>>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n>>> distorted.shape\n(4, 8)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_distorted_grid_polygons(\n    dimensions: np.ndarray,\n    magnitude: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate distorted grid polygons based on input dimensions and magnitude.\n\n    This function creates a grid of polygons and applies random distortions to the internal vertices,\n    while keeping the boundary vertices fixed. The distortion is applied consistently across shared\n    vertices to avoid gaps or overlaps in the resulting grid.\n\n    Args:\n        dimensions (np.ndarray): A 3D array of shape (grid_height, grid_width, 4) where each element\n                                 is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.\n        magnitude (int): Maximum pixel-wise displacement for distortion. The actual displacement\n                         will be randomly chosen in the range [-magnitude, magnitude].\n        random_generator (np.random.Generator): A random number generator.\n\n    Returns:\n        np.ndarray: A 2D array of shape (total_cells, 8) where each row represents a distorted polygon\n                    as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.\n\n    Note:\n        - Only internal grid points are distorted; boundary points remain fixed.\n        - The function ensures consistent distortion across shared vertices of adjacent cells.\n        - The distortion is applied to the following points of each internal cell:\n            * Bottom-right of the cell above and to the left\n            * Bottom-left of the cell above\n            * Top-right of the cell to the left\n            * Top-left of the current cell\n        - Each square represents a cell, and the X marks indicate the coordinates where displacement occurs.\n            +--+--+--+--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--+--+--+--+\n        - For each X, the coordinates of the left, right, top, and bottom edges\n          in the four adjacent cells are displaced.\n\n    Example:\n        >>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n        ...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n        >>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n        >>> distorted.shape\n        (4, 8)\n    \"\"\"\n    grid_height, grid_width = dimensions.shape[:2]\n    total_cells = grid_height * grid_width\n\n    # Initialize polygons\n    polygons = np.zeros((total_cells, 8), dtype=np.float32)\n    polygons[:, 0:2] = dimensions.reshape(-1, 4)[:, [0, 1]]  # x1, y1\n    polygons[:, 2:4] = dimensions.reshape(-1, 4)[:, [2, 1]]  # x2, y1\n    polygons[:, 4:6] = dimensions.reshape(-1, 4)[:, [2, 3]]  # x2, y2\n    polygons[:, 6:8] = dimensions.reshape(-1, 4)[:, [0, 3]]  # x1, y2\n\n    # Generate displacements for internal grid points only\n    internal_points_height, internal_points_width = grid_height - 1, grid_width - 1\n    displacements = random_generator.integers(\n        -magnitude,\n        magnitude + 1,\n        size=(internal_points_height, internal_points_width, 2),\n    ).astype(np.float32)\n\n    # Apply displacements to internal polygon vertices\n    for i in range(1, grid_height):\n        for j in range(1, grid_width):\n            dx, dy = displacements[i - 1, j - 1]\n\n            # Bottom-right of cell (i-1, j-1)\n            polygons[(i - 1) * grid_width + (j - 1), 4:6] += [dx, dy]\n\n            # Bottom-left of cell (i-1, j)\n            polygons[(i - 1) * grid_width + j, 6:8] += [dx, dy]\n\n            # Top-right of cell (i, j-1)\n            polygons[i * grid_width + (j - 1), 2:4] += [dx, dy]\n\n            # Top-left of cell (i, j)\n            polygons[i * grid_width + j, 0:2] += [dx, dy]\n\n    return polygons\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_grid","title":"def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]","text":"

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)\n>>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n>>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n>>> num_steps = 5\n>>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n>>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_grid(\n    image_shape: tuple[int, int],\n    steps_x: list[float],\n    steps_y: list[float],\n    num_steps: int,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate a distorted grid for image transformation based on given step sizes.\n\n    This function creates two 2D arrays (map_x and map_y) that represent a distorted version\n    of the original image grid. These arrays can be used with OpenCV's remap function to\n    apply grid distortion to an image.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        steps_x (list[float]): List of step sizes for the x-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the x direction.\n        steps_y (list[float]): List of step sizes for the y-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the y direction.\n        num_steps (int): The number of steps to divide each axis into. This determines\n            the granularity of the distortion grid.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing two 2D numpy arrays:\n            - map_x: A 2D array of float32 values representing the x-coordinates\n              of the distorted grid.\n            - map_y: A 2D array of float32 values representing the y-coordinates\n              of the distorted grid.\n\n    Note:\n        - The function generates a grid where each cell can be distorted independently.\n        - The distortion is controlled by the steps_x and steps_y parameters, which\n          determine how much each grid line is shifted.\n        - The resulting map_x and map_y can be used directly with cv2.remap() to\n          apply the distortion to an image.\n        - The distortion is applied smoothly across each grid cell using linear\n          interpolation.\n\n    Example:\n        >>> image_shape = (100, 100)\n        >>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n        >>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n        >>> num_steps = 5\n        >>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n        >>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n    \"\"\"\n    height, width = image_shape[:2]\n    x_step = width // num_steps\n    xx = np.zeros(width, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_x):\n        x = idx * x_step\n        start = int(x)\n        end = min(int(x) + x_step, width)\n        cur = prev + x_step * step\n        xx[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    y_step = height // num_steps\n    yy = np.zeros(height, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_y):\n        y = idx * y_step\n        start = int(y)\n        end = min(int(y) + y_step, height)\n        cur = prev + y_step * step\n        yy[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    return np.meshgrid(xx, yy)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_reflected_bboxes","title":"def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_bboxes(\n    bboxes: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected bounding boxes for the entire reflection grid.\n\n    Args:\n        bboxes (np.ndarray): Original bounding boxes.\n        grid_dims (dict[str, tuple[int, int]]): Grid dimensions and original position.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted bounding boxes for the entire grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of bboxes\n    bboxes_hflipped = flip_bboxes(bboxes, flip_horizontal=True, image_shape=image_shape)\n    bboxes_vflipped = flip_bboxes(bboxes, flip_vertical=True, image_shape=image_shape)\n    bboxes_hvflipped = flip_bboxes(\n        bboxes,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [\n            original_col * cols,\n            original_row * rows,\n            original_col * cols,\n            original_row * rows,\n        ],\n    )\n    bboxes = shift_bboxes(bboxes, shift_vector)\n    bboxes_hflipped = shift_bboxes(bboxes_hflipped, shift_vector)\n    bboxes_vflipped = shift_bboxes(bboxes_vflipped, shift_vector)\n    bboxes_hvflipped = shift_bboxes(bboxes_hvflipped, shift_vector)\n\n    new_bboxes = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of bboxes to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes\n            elif (grid_row - original_row) % 2 == 0:\n                current_bboxes = bboxes_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes_vflipped\n            else:\n                current_bboxes = bboxes_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                ],\n            )\n            shifted_bboxes = shift_bboxes(current_bboxes, cell_shift)\n\n            new_bboxes.append(shifted_bboxes)\n\n    result = np.vstack(new_bboxes)\n\n    return shift_bboxes(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_reflected_keypoints","title":"def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols) - \"original_position\": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_keypoints(\n    keypoints: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected keypoints for the entire reflection grid.\n\n    This function creates a grid of keypoints by reflecting and shifting the original keypoints.\n    It handles both centered and non-centered grids based on the `center_in_origin` parameter.\n\n    Args:\n        keypoints (np.ndarray): Original keypoints array of shape (N, 4+), where N is the number of keypoints,\n                                and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).\n        grid_dims (dict[str, tuple[int, int]]): A dictionary containing grid dimensions and original position.\n            It should have the following keys:\n            - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols)\n            - \"original_position\": tuple[int, int] representing (original_row, original_col)\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool, optional): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted keypoints for the entire grid. The shape is\n                    (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.\n\n    Note:\n        - The function handles keypoint flipping and shifting to create a grid of reflected keypoints.\n        - It preserves the angle and scale information of the keypoints during transformations.\n        - The resulting grid can be either centered at the origin or positioned based on the original grid.\n    \"\"\"\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of keypoints\n    keypoints_hflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        image_shape=image_shape,\n    )\n    keypoints_vflipped = flip_keypoints(\n        keypoints,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n    keypoints_hvflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    rows, cols = image_shape[:2]\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [original_col * cols, original_row * rows, 0, 0, 0],\n    )  # Only shift x and y\n    keypoints = shift_keypoints(keypoints, shift_vector)\n    keypoints_hflipped = shift_keypoints(keypoints_hflipped, shift_vector)\n    keypoints_vflipped = shift_keypoints(keypoints_vflipped, shift_vector)\n    keypoints_hvflipped = shift_keypoints(keypoints_hvflipped, shift_vector)\n\n    new_keypoints = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of keypoints to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints\n            elif (grid_row - original_row) % 2 == 0:\n                current_keypoints = keypoints_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints_vflipped\n            else:\n                current_keypoints = keypoints_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    0,\n                    0,\n                    0,\n                ],\n            )\n            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)\n\n            new_keypoints.append(shifted_keypoints)\n\n    result = np.vstack(new_keypoints)\n\n    return shift_keypoints(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.generate_shuffled_splits","title":"def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]","text":"

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_shuffled_splits(\n    size: int,\n    divisions: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate shuffled splits for a given dimension size and number of divisions.\n\n    Args:\n        size (int): Total size of the dimension (height or width).\n        divisions (int): Number of divisions (rows or columns).\n        random_generator (np.random.Generator | None): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: Cumulative edges of the shuffled intervals.\n    \"\"\"\n    intervals = almost_equal_intervals(size, divisions)\n    random_generator.shuffle(intervals)\n    return np.insert(np.cumsum(intervals), 0, 0)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_camera_matrix_distortion_maps","title":"def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_camera_matrix_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using camera matrix model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n    camera_matrix = np.array(\n        [[width, 0, center_xy[0]], [0, height, center_xy[1]], [0, 0, 1]],\n        dtype=np.float32,\n    )\n    distortion = np.array([k, k, 0, 0, 0], dtype=np.float32)\n    return cv2.initUndistortRectifyMap(\n        camera_matrix,\n        distortion,\n        None,\n        None,\n        (width, height),\n        cv2.CV_32FC1,\n    )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_dimension_padding","title":"def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]","text":"

Calculate padding for a single dimension.

Parameters:

Name Type Description current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_dimension_padding(\n    current_size: int,\n    min_size: int | None,\n    divisor: int | None,\n) -> tuple[int, int]:\n    \"\"\"Calculate padding for a single dimension.\n\n    Args:\n        current_size: Current size of the dimension\n        min_size: Minimum size requirement, if any\n        divisor: Divisor for padding to make size divisible, if any\n\n    Returns:\n        tuple[int, int]: (pad_before, pad_after)\n    \"\"\"\n    if min_size is not None:\n        if current_size < min_size:\n            pad_before = int((min_size - current_size) / 2.0)\n            pad_after = min_size - current_size - pad_before\n            return pad_before, pad_after\n    elif divisor is not None:\n        remainder = current_size % divisor\n        if remainder > 0:\n            total_pad = divisor - remainder\n            pad_before = total_pad // 2\n            pad_after = total_pad - pad_before\n            return pad_before, pad_after\n\n    return 0, 0\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_fisheye_distortion_maps","title":"def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using fisheye model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_fisheye_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using fisheye model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n\n    center_x, center_y = center_xy\n\n    # Create coordinate grid\n    y, x = np.mgrid[:height, :width].astype(np.float32)\n\n    x = x - center_x\n    y = y - center_y\n\n    # Calculate polar coordinates\n    r = np.sqrt(x * x + y * y)\n    theta = np.arctan2(y, x)\n\n    # Normalize radius by the maximum possible radius to keep distortion in check\n    max_radius = math.sqrt(max(center_x, width - center_x) ** 2 + max(center_y, height - center_y) ** 2)\n    r_norm = r / max_radius\n\n    # Apply fisheye distortion to normalized radius\n    r_dist = r * (1 + k * r_norm * r_norm)\n\n    # Convert back to cartesian coordinates\n    map_x = r_dist * np.cos(theta) + center_x\n    map_y = r_dist * np.sin(theta) + center_y\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_pad_grid_dimensions","title":"def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]","text":"

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def get_pad_grid_dimensions(\n    pad_top: int,\n    pad_bottom: int,\n    pad_left: int,\n    pad_right: int,\n    image_shape: tuple[int, int],\n) -> dict[str, tuple[int, int]]:\n    \"\"\"Calculate the dimensions of the grid needed for reflection padding and the position of the original image.\n\n    Args:\n        pad_top (int): Number of pixels to pad above the image.\n        pad_bottom (int): Number of pixels to pad below the image.\n        pad_left (int): Number of pixels to pad to the left of the image.\n        pad_right (int): Number of pixels to pad to the right of the image.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n\n    Returns:\n        dict[str, tuple[int, int]]: A dictionary containing:\n            - 'grid_shape': A tuple (grid_rows, grid_cols) where:\n                - grid_rows (int): Number of times the image needs to be repeated vertically.\n                - grid_cols (int): Number of times the image needs to be repeated horizontally.\n            - 'original_position': A tuple (original_row, original_col) where:\n                - original_row (int): Row index of the original image in the grid.\n                - original_col (int): Column index of the original image in the grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    grid_rows = 1 + math.ceil(pad_top / rows) + math.ceil(pad_bottom / rows)\n    grid_cols = 1 + math.ceil(pad_left / cols) + math.ceil(pad_right / cols)\n    original_row = math.ceil(pad_top / rows)\n    original_col = math.ceil(pad_left / cols)\n\n    return {\n        \"grid_shape\": (grid_rows, grid_cols),\n        \"original_position\": (original_row, original_col),\n    }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.get_padding_params","title":"def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]","text":"

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_padding_params(\n    image_shape: tuple[int, int],\n    min_height: int | None,\n    min_width: int | None,\n    pad_height_divisor: int | None,\n    pad_width_divisor: int | None,\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate padding parameters based on target dimensions.\n\n    Args:\n        image_shape: (height, width) of the image\n        min_height: Minimum height requirement, if any\n        min_width: Minimum width requirement, if any\n        pad_height_divisor: Divisor for height padding, if any\n        pad_width_divisor: Divisor for width padding, if any\n\n    Returns:\n        tuple[int, int, int, int]: (pad_top, pad_bottom, pad_left, pad_right)\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    h_pad_top, h_pad_bottom = get_dimension_padding(\n        rows,\n        min_height,\n        pad_height_divisor,\n    )\n    w_pad_left, w_pad_right = get_dimension_padding(cols, min_width, pad_width_divisor)\n\n    return h_pad_top, h_pad_bottom, w_pad_left, w_pad_right\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.is_identity_matrix","title":"def is_identity_matrix (matrix) [view source on GitHub]","text":"

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_identity_matrix(matrix: np.ndarray) -> bool:\n    \"\"\"Check if the given matrix is an identity matrix.\n\n    Args:\n        matrix (np.ndarray): A 3x3 affine transformation matrix.\n\n    Returns:\n        bool: True if the matrix is an identity matrix, False otherwise.\n    \"\"\"\n    return np.allclose(matrix, np.eye(3, dtype=matrix.dtype))\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.is_valid_component","title":"def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]","text":"

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_valid_component(\n    component_area: float,\n    original_area: float,\n    min_area: float | None,\n    min_visibility: float | None,\n) -> bool:\n    \"\"\"Validate if a component meets the minimum requirements.\"\"\"\n    visibility = component_area / original_area\n    return (min_area is None or component_area >= min_area) and (min_visibility is None or visibility >= min_visibility)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_affine","title":"def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]","text":"

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])\n>>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n>>> scale = {'x': 1.5, 'y': 1.2}\n>>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_affine(\n    keypoints: np.ndarray,\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n    scale: XYFloat,\n    border_mode: int,\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to keypoints.\n\n    This function transforms keypoints using the given affine transformation matrix.\n    It handles reflection padding if necessary, updates coordinates, angles, and scales.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where N is the number of keypoints.\n                                Each keypoint is represented as [x, y, angle, scale, ...].\n        matrix (np.ndarray): The 2x3 or 3x3 affine transformation matrix.\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        scale (dict[str, float]): Dictionary containing scale factors for x and y directions.\n                                  Expected keys are 'x' and 'y'.\n        border_mode (int): Border mode for handling keypoints near image edges.\n                            Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.\n\n    Returns:\n        np.ndarray: Transformed keypoints array with the same shape as input.\n\n    Notes:\n        - The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.\n        - Coordinates (x, y) are transformed using the affine matrix.\n        - Angles are adjusted based on the rotation component of the affine transformation.\n        - Scales are multiplied by the maximum of x and y scale factors.\n        - The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.\n\n    Example:\n        >>> keypoints = np.array([[100, 100, 0, 1]])\n        >>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n        >>> scale = {'x': 1.5, 'y': 1.2}\n        >>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    if is_identity_matrix(matrix):\n        return keypoints\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        keypoints = generate_reflected_keypoints(\n            keypoints,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Extract x, y coordinates (z is preserved)\n    xy = keypoints[:, :2]\n\n    # Ensure matrix is 2x3\n    if matrix.shape == (3, 3):\n        matrix = matrix[:2]\n\n    # Transform x, y coordinates\n    xy_transformed = cv2.transform(xy.reshape(-1, 1, 2), matrix).squeeze()\n\n    # Calculate angle adjustment\n    angle_adjustment = rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=False)\n\n    # Update angles (now at index 3)\n    keypoints[:, 3] = keypoints[:, 3] + angle_adjustment\n\n    # Update scales (now at index 4)\n    max_scale = max(scale[\"x\"], scale[\"y\"])\n    keypoints[:, 4] *= max_scale\n\n    # Update x, y coordinates and preserve z\n    keypoints[:, :2] = xy_transformed\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_d4","title":"def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_d4(\n    keypoints: np.ndarray,\n    group_member: D4Type,\n    image_shape: tuple[int, int],\n    **params: Any,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a keypoint.\n\n    This function adjusts a keypoint's coordinates according to the specified `D_4` group transformation,\n    which includes rotations and reflections suitable for image processing tasks. These transformations account\n    for the dimensions of the image to ensure the keypoint remains within its boundaries.\n\n    Parameters:\n    - keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n    -group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.\n    - image_shape (tuple[int, int]): The shape of the image.\n    - params (Any): Not used\n\n    Returns:\n    - KeypointInternalType: The transformed keypoint.\n\n    Raises:\n    - ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.\n\n    Examples:\n    - Rotating a keypoint by 90 degrees in a 100x100 image:\n      `keypoint_d4((50, 30), 'r90', 100, 100)`\n      This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: keypoints_rot90(x, 1, image_shape),  # Rotate 90 degrees\n        \"r180\": lambda x: keypoints_rot90(x, 2, image_shape),  # Rotate 180 degrees\n        \"r270\": lambda x: keypoints_rot90(x, 3, image_shape),  # Rotate 270 degrees\n        \"v\": lambda x: keypoints_vflip(x, rows),  # Vertical flip\n        \"hvt\": lambda x: keypoints_transpose(\n            keypoints_rot90(x, 2, image_shape),\n        ),  # Reflect over anti diagonal\n        \"h\": lambda x: keypoints_hflip(x, cols),  # Horizontal flip\n        \"t\": lambda x: keypoints_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](keypoints)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_hflip","title":"def keypoints_hflip (keypoints, cols) [view source on GitHub]","text":"

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_hflip(keypoints: np.ndarray, cols: int) -> np.ndarray:\n    \"\"\"Flip keypoints horizontally around the y-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        cols: Image width.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip x-coordinates\n    flipped_keypoints[:, 0] = (cols - 1) - keypoints[:, 0]\n\n    # Adjust angles\n    flipped_keypoints[:, 3] = np.pi - keypoints[:, 3]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_rot90","title":"def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]","text":"

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description np.ndarray

The rotated keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_rot90(\n    keypoints: np.ndarray,\n    factor: Literal[0, 1, 2, 3],\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        factor (int): The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n\n    Returns:\n        np.ndarray: The rotated keypoints with the same shape as the input.\n    \"\"\"\n    if factor == 0:\n        return keypoints\n\n    height, width = image_shape[:2]\n    rotated_keypoints = keypoints.copy().astype(np.float32)\n\n    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 3]\n\n    if factor == 1:\n        rotated_keypoints[:, 0] = y\n        rotated_keypoints[:, 1] = width - 1 - x\n        rotated_keypoints[:, 3] = angle - np.pi / 2\n    elif factor == ROT90_180_FACTOR:\n        rotated_keypoints[:, 0] = width - 1 - x\n        rotated_keypoints[:, 1] = height - 1 - y\n        rotated_keypoints[:, 3] = angle - np.pi\n    elif factor == ROT90_270_FACTOR:\n        rotated_keypoints[:, 0] = height - 1 - y\n        rotated_keypoints[:, 1] = x\n        rotated_keypoints[:, 3] = angle + np.pi / 2\n\n    return rotated_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_scale","title":"def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]","text":"

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of keypoints with shape (N, 5+) in the format (x, y, z, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description np.ndarray

A numpy array of scaled keypoints with the same shape as input. X and Y coordinates are scaled by their respective scale factors, Z coordinate remains unchanged, and the keypoint scale is multiplied by max(scale_x, scale_y).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_scale(\n    keypoints: np.ndarray,\n    scale_x: float,\n    scale_y: float,\n) -> np.ndarray:\n    \"\"\"Scales keypoints by scale_x and scale_y.\n\n    Args:\n        keypoints: A numpy array of keypoints with shape (N, 5+) in the format\n                  (x, y, z, angle, scale, ...).\n        scale_x: Scale coefficient x-axis.\n        scale_y: Scale coefficient y-axis.\n\n    Returns:\n        A numpy array of scaled keypoints with the same shape as input.\n        X and Y coordinates are scaled by their respective scale factors,\n        Z coordinate remains unchanged, and the keypoint scale is multiplied\n        by max(scale_x, scale_y).\n    \"\"\"\n    # Extract x, y, z, angle, and scale\n    x, y, z, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n        keypoints[:, 4],\n    )\n\n    # Scale x and y\n    x_scaled = x * scale_x\n    y_scaled = y * scale_y\n\n    # Scale the keypoint scale by the maximum of scale_x and scale_y\n    scale_scaled = scale * max(scale_x, scale_y)\n\n    # Create the output array\n    scaled_keypoints = np.column_stack([x_scaled, y_scaled, z, angle, scale_scaled])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],\n        )\n\n    return scaled_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_transpose","title":"def keypoints_transpose (keypoints) [view source on GitHub]","text":"

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_transpose(keypoints: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes keypoints along the main diagonal.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n\n    Returns:\n        np.ndarray: An array of transposed keypoints with the same shape as the input.\n    \"\"\"\n    transposed_keypoints = keypoints.copy()\n\n    # Swap x and y coordinates\n    transposed_keypoints[:, [0, 1]] = keypoints[:, [1, 0]]\n\n    # Adjust angles to reflect the coordinate swap\n    angles = keypoints[:, 3]\n    transposed_keypoints[:, 3] = np.where(\n        angles <= np.pi,\n        np.pi / 2 - angles,\n        3 * np.pi / 2 - angles,\n    )\n\n    return transposed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.keypoints_vflip","title":"def keypoints_vflip (keypoints, rows) [view source on GitHub]","text":"

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_vflip(keypoints: np.ndarray, rows: int) -> np.ndarray:\n    \"\"\"Flip keypoints vertically around the x-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        rows: Image height.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip y-coordinates\n    flipped_keypoints[:, 1] = (rows - 1) - keypoints[:, 1]\n\n    # Negate angles\n    flipped_keypoints[:, 3] = -keypoints[:, 3]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.perspective_bboxes","title":"def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n>>> image_shape = (100, 100)\n>>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n>>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef perspective_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Applies perspective transformation to bounding boxes.\n\n    This function transforms bounding boxes using the given perspective transformation matrix.\n    It handles bounding boxes with additional attributes beyond the standard coordinates.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (num_bboxes, 4+).\n                             Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n                             Additional columns beyond the first 4 are preserved unchanged.\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        matrix (np.ndarray): The perspective transformation matrix.\n        max_width (int): The maximum width of the output image.\n        max_height (int): The maximum height of the output image.\n        keep_size (bool): If True, maintains the original image size after transformation.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as input.\n                    The first 4 columns contain the transformed coordinates, and any\n                    additional columns are preserved from the input.\n\n    Note:\n        - This function modifies only the coordinate columns (first 4) of the input bounding boxes.\n        - Any additional attributes (columns beyond the first 4) are kept unchanged.\n        - The function handles denormalization and renormalization of coordinates internally.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n        >>> image_shape = (100, 100)\n        >>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n        >>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n    \"\"\"\n    height, width = image_shape[:2]\n    transformed_bboxes = bboxes.copy()\n    denormalized_coords = denormalize_bboxes(bboxes[:, :4], image_shape)\n\n    x_min, y_min, x_max, y_max = denormalized_coords.T\n    points = np.array(\n        [[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]],\n    ).transpose(2, 0, 1)\n    points_reshaped = points.reshape(-1, 1, 2)\n\n    transformed_points = cv2.perspectiveTransform(\n        points_reshaped.astype(np.float32),\n        matrix,\n    )\n    transformed_points = transformed_points.reshape(-1, 4, 2)\n\n    new_coords = np.array(\n        [[np.min(box[:, 0]), np.min(box[:, 1]), np.max(box[:, 0]), np.max(box[:, 1])] for box in transformed_points],\n    )\n\n    if keep_size:\n        scale_x, scale_y = width / max_width, height / max_height\n        new_coords[:, [0, 2]] *= scale_x\n        new_coords[:, [1, 3]] *= scale_y\n        output_shape = image_shape\n    else:\n        output_shape = (max_height, max_width)\n\n    normalized_coords = normalize_bboxes(new_coords, output_shape)\n    transformed_bboxes[:, :4] = normalized_coords\n\n    return transformed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.perspective_keypoints","title":"def perspective_keypoints (keypoints, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Apply perspective transformation to keypoints.

Parameters:

Name Type Description keypoints np.ndarray

Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].

image_shape tuple[int, int]

Original image shape (height, width).

matrix np.ndarray

3x3 perspective transformation matrix.

max_width int

Maximum width after transformation.

max_height int

Maximum height after transformation.

keep_size bool

Whether to keep original size.

Returns:

Type Description np.ndarray

Transformed keypoints array with same shape as input. Z coordinate remains unchanged through the transformation.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef perspective_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Apply perspective transformation to keypoints.\n\n    Args:\n        keypoints: Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].\n        image_shape: Original image shape (height, width).\n        matrix: 3x3 perspective transformation matrix.\n        max_width: Maximum width after transformation.\n        max_height: Maximum height after transformation.\n        keep_size: Whether to keep original size.\n\n    Returns:\n        Transformed keypoints array with same shape as input.\n        Z coordinate remains unchanged through the transformation.\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    height, width = image_shape[:2]\n\n    x, y, z, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n        keypoints[:, 4],\n    )\n\n    # Reshape keypoints for perspective transform\n    keypoint_vector = np.column_stack((x, y)).astype(np.float32).reshape(-1, 1, 2)\n\n    # Apply perspective transform\n    transformed_points = cv2.perspectiveTransform(keypoint_vector, matrix).squeeze()\n\n    # Unsqueeze if we have a single keypoint\n    if transformed_points.ndim == 1:\n        transformed_points = transformed_points[np.newaxis, :]\n\n    x, y = transformed_points[:, 0], transformed_points[:, 1]\n\n    # Update angles\n    angle += rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=True)\n\n    # Calculate scale factors\n    scale_x = np.sign(matrix[0, 0]) * np.sqrt(matrix[0, 0] ** 2 + matrix[0, 1] ** 2)\n    scale_y = np.sign(matrix[1, 1]) * np.sqrt(matrix[1, 0] ** 2 + matrix[1, 1] ** 2)\n    scale *= max(scale_x, scale_y)\n\n    if keep_size:\n        scale_x = width / max_width\n        scale_y = height / max_height\n        x *= scale_x\n        y *= scale_y\n        scale *= max(scale_x, scale_y)\n\n    # Create the output array with unchanged z coordinate\n    transformed_keypoints = np.column_stack([x, y, z, angle, scale])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [\n                transformed_keypoints,\n                keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:],\n            ],\n        )\n\n    return transformed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.rotation2d_matrix_to_euler_angles","title":"def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]","text":"

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:\n    \"\"\"Args:\n    matrix (np.ndarray): Rotation matrix\n    y_up (bool): is Y axis looks up or down\n\n    \"\"\"\n    if y_up:\n        return np.arctan2(matrix[1, 0], matrix[0, 0])\n    return np.arctan2(-matrix[1, 0], matrix[0, 0])\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.shift_bboxes","title":"def shift_bboxes (bboxes, shift_vector) [view source on GitHub]","text":"

Shift bounding boxes by a given vector.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Shift bounding boxes by a given vector.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where n is the number of bboxes\n                             and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].\n        shift_vector (np.ndarray): Vector to shift the bounding boxes by, with shape (4,) for\n                                   [shift_x, shift_y, shift_x, shift_y].\n\n    Returns:\n        np.ndarray: Shifted bounding boxes with the same shape as input.\n    \"\"\"\n    # Create a copy of the input array to avoid modifying it in-place\n    shifted_bboxes = bboxes.copy()\n\n    # Add the shift vector to the first 4 columns\n    shifted_bboxes[:, :4] += shift_vector\n\n    return shifted_bboxes\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.shuffle_tiles_within_shape_groups","title":"def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]","text":"

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py Python
def shuffle_tiles_within_shape_groups(\n    shape_groups: dict[tuple[int, int], list[int]],\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Shuffles indices within each group of similar shapes and creates a list where each\n    index points to the index of the tile it should be mapped to.\n\n    Args:\n        shape_groups (dict[tuple[int, int], list[int]]): Groups of tile indices categorized by shape.\n        random_generator (np.random.Generator): The random generator to use for shuffling the indices.\n            If None, a new random generator will be used.\n\n    Returns:\n        list[int]: A list where each index is mapped to the new index of the tile after shuffling.\n    \"\"\"\n    # Initialize the output list with the same size as the total number of tiles, filled with -1\n    num_tiles = sum(len(indices) for indices in shape_groups.values())\n    mapping = [-1] * num_tiles\n\n    # Prepare the random number generator\n\n    for indices in shape_groups.values():\n        shuffled_indices = indices.copy()\n        random_generator.shuffle(shuffled_indices)\n\n        for old, new in zip(indices, shuffled_indices):\n            mapping[old] = new\n\n    return mapping\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.split_uniform_grid","title":"def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]","text":"

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py Python
def split_uniform_grid(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Splits an image shape into a uniform grid specified by the grid dimensions.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns).\n        random_generator (np.random.Generator): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).\n\n    Note:\n        The function uses `generate_shuffled_splits` to generate the splits for the height and width of the image.\n        The splits are then used to calculate the coordinates of the tiles.\n    \"\"\"\n    n_rows, n_cols = grid\n\n    height_splits = generate_shuffled_splits(\n        image_shape[0],\n        grid[0],\n        random_generator=random_generator,\n    )\n    width_splits = generate_shuffled_splits(\n        image_shape[1],\n        grid[1],\n        random_generator=random_generator,\n    )\n\n    # Calculate tiles coordinates\n    tiles = [\n        (height_splits[i], width_splits[j], height_splits[i + 1], width_splits[j + 1])\n        for i in range(n_rows)\n        for j in range(n_cols)\n    ]\n\n    return np.array(tiles, dtype=np.int16)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.swap_tiles_on_image","title":"def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]","text":"

Swap tiles on the image according to the new format.

Parameters:

Name Type Description image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_image(\n    image: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int] | None = None,\n) -> np.ndarray:\n    \"\"\"Swap tiles on the image according to the new format.\n\n    Args:\n        image: Input image.\n        tiles: Array of tiles with each tile as [start_y, start_x, end_y, end_x].\n        mapping: list of new tile indices.\n\n    Returns:\n        np.ndarray: Output image with tiles swapped according to the random shuffle.\n    \"\"\"\n    # If no tiles are provided, return a copy of the original image\n    if tiles.size == 0 or mapping is None:\n        return image.copy()\n\n    # Create a copy of the image to retain original for reference\n    new_image = np.empty_like(image)\n    for num, new_index in enumerate(mapping):\n        start_y, start_x, end_y, end_x = tiles[new_index]\n        start_y_orig, start_x_orig, end_y_orig, end_x_orig = tiles[num]\n        # Assign the corresponding tile from the original image to the new image\n        new_image[start_y:end_y, start_x:end_x] = image[\n            start_y_orig:end_y_orig,\n            start_x_orig:end_x_orig,\n        ]\n\n    return new_image\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.swap_tiles_on_keypoints","title":"def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]","text":"

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_keypoints(\n    keypoints: np.ndarray,\n    tiles: np.ndarray,\n    mapping: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Swap the positions of keypoints based on a tile mapping.\n\n    This function takes a set of keypoints and repositions them according to a mapping of tile swaps.\n    Keypoints are moved from their original tiles to new positions in the swapped tiles.\n\n    Args:\n        keypoints (np.ndarray): A 2D numpy array of shape (N, 2) where N is the number of keypoints.\n                                Each row represents a keypoint's (x, y) coordinates.\n        tiles (np.ndarray): A 2D numpy array of shape (M, 4) where M is the number of tiles.\n                            Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.\n        mapping (np.ndarray): A 1D numpy array of shape (M,) where M is the number of tiles.\n                              Each element i contains the index of the tile that tile i should be swapped with.\n\n    Returns:\n        np.ndarray: A 2D numpy array of the same shape as the input keypoints, containing the new positions\n                    of the keypoints after the tile swap.\n\n    Raises:\n        RuntimeWarning: If any keypoint is not found within any tile.\n\n    Notes:\n        - Keypoints that do not fall within any tile will remain unchanged.\n        - The function assumes that the tiles do not overlap and cover the entire image space.\n    \"\"\"\n    if not keypoints.size:\n        return keypoints\n\n    # Broadcast keypoints and tiles for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    start_y, start_x, end_y, end_x = tiles.T  # Each shape: (num_tiles,)\n\n    # Check if each keypoint is inside each tile\n    in_tile = (kp_y >= start_y) & (kp_y < end_y) & (kp_x >= start_x) & (kp_x < end_x)\n\n    # Find which tile each keypoint belongs to\n    tile_indices = np.argmax(in_tile, axis=1)\n\n    # Check if any keypoint is not in any tile\n    not_in_any_tile = ~np.any(in_tile, axis=1)\n    if np.any(not_in_any_tile):\n        warn(\n            \"Some keypoints are not in any tile. They will be returned unchanged. This is unexpected and should be \"\n            \"investigated.\",\n            RuntimeWarning,\n            stacklevel=2,\n        )\n\n    # Get the new tile indices\n    new_tile_indices = np.array(mapping)[tile_indices]\n\n    # Calculate the offsets\n    old_start_x = tiles[tile_indices, 1]\n    old_start_y = tiles[tile_indices, 0]\n    new_start_x = tiles[new_tile_indices, 1]\n    new_start_y = tiles[new_tile_indices, 0]\n\n    # Apply the transformation\n    new_keypoints = keypoints.copy()\n    new_keypoints[:, 0] = (keypoints[:, 0] - old_start_x) + new_start_x\n    new_keypoints[:, 1] = (keypoints[:, 1] - old_start_y) + new_start_y\n\n    # Keep original coordinates for keypoints not in any tile\n    new_keypoints[not_in_any_tile] = keypoints[not_in_any_tile]\n\n    return new_keypoints\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.to_distance_maps","title":"def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]","text":"

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def to_distance_maps(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    inverted: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate a ``(H,W,N)`` array of distance maps for ``N`` keypoints.\n\n    The ``n``-th distance map contains at every location ``(y, x)`` the\n    euclidean distance to the ``n``-th keypoint.\n\n    This function can be used as a helper when augmenting keypoints with a\n    method that only supports the augmentation of images.\n\n    Args:\n        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.\n                   Each row represents a keypoint's (x, y) coordinates.\n        image_shape: tuple[int, int] shape of the image (height, width)\n        inverted (bool): If ``True``, inverted distance maps are returned where each\n            distance value d is replaced by ``d/(d+1)``, i.e. the distance\n            maps have values in the range ``(0.0, 1.0]`` with ``1.0`` denoting\n            exactly the position of the respective keypoint.\n\n    Returns:\n        np.ndarray: A ``float32`` array of shape (H, W, N) containing ``N`` distance maps for ``N``\n            keypoints. Each location ``(y, x, n)`` in the array denotes the\n            euclidean distance at ``(y, x)`` to the ``n``-th keypoint.\n            If `inverted` is ``True``, the distance ``d`` is replaced\n            by ``d/(d+1)``. The height and width of the array match the\n            height and width in ``image_shape``.\n    \"\"\"\n    height, width = image_shape[:2]\n    if len(keypoints) == 0:\n        return np.zeros((height, width, 0), dtype=np.float32)\n\n    # Create coordinate grids\n    yy, xx = np.mgrid[:height, :width]\n\n    # Convert keypoints to numpy array\n    keypoints_array = np.array(keypoints)\n\n    # Compute distances for all keypoints at once\n    distances = np.sqrt(\n        (xx[..., np.newaxis] - keypoints_array[:, 0]) ** 2 + (yy[..., np.newaxis] - keypoints_array[:, 1]) ** 2,\n    )\n\n    if inverted:\n        return (1 / (distances + 1)).astype(np.float32)\n    return distances.astype(np.float32)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.tps_transform","title":"def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]","text":"

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def tps_transform(\n    target_points: np.ndarray,\n    control_points: np.ndarray,\n    nonlinear_weights: np.ndarray,\n    affine_weights: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply Thin Plate Spline transformation to points.\n\n    Args:\n        target_points: Points to transform with shape (num_targets, 2)\n        control_points: Original control points with shape (num_controls, 2)\n        nonlinear_weights: TPS kernel weights with shape (num_controls, 2)\n        affine_weights: Affine transformation weights with shape (3, 2)\n\n    Returns:\n        Transformed points with shape (num_targets, 2)\n\n    Note:\n        The transformation combines:\n        1. Nonlinear warping based on distances to control points\n        2. Global affine transformation (scale, rotation, translation)\n    \"\"\"\n    # Compute all pairwise distances at once: (num_targets, num_controls)\n    distances = np.linalg.norm(target_points[:, None] - control_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Prepare affine terms [1, x, y] for each point\n    affine_terms = np.c_[np.ones(len(target_points)), target_points]\n\n    # Combine nonlinear and affine transformations\n    return kernel_matrix @ nonlinear_weights + affine_terms @ affine_weights\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.transpose","title":"def transpose (img) [view source on GitHub]","text":"

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description img np.ndarray

Input array.

Returns:

Type Description np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py Python
def transpose(img: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes the first two dimensions of an array of any dimensionality.\n    Retains the order of any additional dimensions.\n\n    Args:\n        img (np.ndarray): Input array.\n\n    Returns:\n        np.ndarray: Transposed array.\n    \"\"\"\n    # Generate the new axes order\n    new_axes = list(range(img.ndim))\n    new_axes[0], new_axes[1] = 1, 0  # Swap the first two dimensions\n\n    # Transpose the array using the new axes order\n    return img.transpose(new_axes)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_bboxes","title":"def validate_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n>>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n>>> print(valid_bboxes)\n[[10 20 30 40]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
def validate_bboxes(bboxes: np.ndarray, image_shape: Sequence[int]) -> np.ndarray:\n    \"\"\"Validate bounding boxes and remove invalid ones.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid bounding boxes, potentially with fewer boxes than the input.\n\n    Example:\n        >>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n        >>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n        >>> print(valid_bboxes)\n        [[10 20 30 40]]\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    valid_indices = (x_max > 0) & (y_max > 0) & (x_min < cols) & (y_min < rows)\n\n    return bboxes[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_if_not_found_coords","title":"def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]","text":"

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_if_not_found_coords(\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None,\n) -> tuple[bool, float, float]:\n    \"\"\"Validate and process `if_not_found_coords` parameter.\"\"\"\n    if if_not_found_coords is None:\n        return True, -1, -1\n    if isinstance(if_not_found_coords, (tuple, list)):\n        if len(if_not_found_coords) != PAIR:\n            msg = \"Expected tuple/list 'if_not_found_coords' to contain exactly two entries.\"\n            raise ValueError(msg)\n        return False, if_not_found_coords[0], if_not_found_coords[1]\n    if isinstance(if_not_found_coords, dict):\n        return False, if_not_found_coords[\"x\"], if_not_found_coords[\"y\"]\n\n    msg = \"Expected if_not_found_coords to be None, tuple, list, or dict.\"\n    raise ValueError(msg)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.functional.validate_keypoints","title":"def validate_keypoints (keypoints, image_shape) [view source on GitHub]","text":"

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Validate keypoints and remove those that fall outside the image boundaries.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, M) where N is the number of keypoints\n                                and M >= 2. The first two columns represent x and y coordinates.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid keypoints that fall within the image boundaries.\n\n    Note:\n        This function only checks the x and y coordinates (first two columns) of the keypoints.\n        Any additional columns (e.g., angle, scale) are preserved for valid keypoints.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x, y = keypoints[:, 0], keypoints[:, 1]\n\n    valid_indices = (x >= 0) & (x < cols) & (y >= 0) & (y < rows)\n\n    return keypoints[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize","title":"resize","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.LongestMaxSize","title":"class LongestMaxSize [view source on GitHub]","text":"

Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, Sequence[int]

Maximum size of the longest side after the transformation. When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must fit within these bounds - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

interpolation method. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the longest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / max(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = min(H_target/H, W_target/W)\n       This ensures both dimensions fit within the specified bounds.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> # Using max_size\n>>> transform1 = A.LongestMaxSize(max_size=1024)\n>>> # Input image (1500, 800) -> Output (1024, 546)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>> # Input (800, 1500) -> Output (546, 1024)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>>\n>>> # Common use case with padding\n>>> transform4 = A.Compose([\n...     A.LongestMaxSize(max_size=1024),\n...     A.PadIfNeeded(min_height=1024, min_width=1024),\n... ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class LongestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints,\n        keeping the aspect ratio.\n\n    Args:\n        max_size (int, Sequence[int], optional): Maximum size of the longest side after the transformation.\n            When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must fit within these bounds\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): interpolation method. Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the longest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / max(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = min(H_target/H, W_target/W)\n               This ensures both dimensions fit within the specified bounds.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> # Using max_size\n        >>> transform1 = A.LongestMaxSize(max_size=1024)\n        >>> # Input image (1500, 800) -> Output (1024, 546)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>> # Input (800, 1500) -> Output (546, 1024)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>>\n        >>> # Common use case with padding\n        >>> transform4 = A.Compose([\n        ...     A.LongestMaxSize(max_size=1024),\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024),\n        ... ])\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / max(img_h, img_w)\n        elif self.max_size_hw is not None:\n            # We know max_size_hw is not None here due to model validator\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on longest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = min(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.MaxSizeTransform","title":"class MaxSizeTransform (max_size=1024, max_size_hw=None, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that resize based on maximum size constraints.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class MaxSizeTransform(DualTransform):\n    \"\"\"Base class for transforms that resize based on maximum size constraints.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_size: int | list[int] | None\n        max_size_hw: tuple[int | None, int | None] | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def validate_size_parameters(self) -> Self:\n            if self.max_size is None and self.max_size_hw is None:\n                raise ValueError(\"Either max_size or max_size_hw must be specified\")\n            if self.max_size is not None and self.max_size_hw is not None:\n                raise ValueError(\"Only one of max_size or max_size_hw should be specified\")\n            return self\n\n    def __init__(\n        self,\n        max_size: int | Sequence[int] | None = 1024,\n        max_size_hw: tuple[int | None, int | None] | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_size = max_size\n        self.max_size_hw = max_size_hw\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = img.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(img, (new_height, new_width), interpolation=self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = mask.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(mask, (new_height, new_width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(images, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volume, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volumes, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(masks3d, *args, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_size\", \"max_size_hw\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.RandomScale","title":"class RandomScale (scale_limit=(-0.1, 0.1), interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly resize the input. Output image size is different from the input image size.

Parameters:

Name Type Description scale_limit float or tuple[float, float]

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The output image size is different from the input image size.
  • Scale factor is sampled independently per image side (width and height).
  • Bounding box coordinates are scaled accordingly.
  • Keypoint coordinates are scaled accordingly.

Mathematical formulation: Let (W, H) be the original image dimensions and (W', H') be the output dimensions. The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]]. Then, W' = W * s and H' = H * s.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> scaled_image = result['image']\n# scaled_image will have dimensions in the range [90, 110] x [90, 110]\n# (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class RandomScale(DualTransform):\n    \"\"\"Randomly resize the input. Output image size is different from the input image size.\n\n    Args:\n        scale_limit (float or tuple[float, float]): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The output image size is different from the input image size.\n        - Scale factor is sampled independently per image side (width and height).\n        - Bounding box coordinates are scaled accordingly.\n        - Keypoint coordinates are scaled accordingly.\n\n    Mathematical formulation:\n        Let (W, H) be the original image dimensions and (W', H') be the output dimensions.\n        The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]].\n        Then, W' = W * s and H' = H * s.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> scaled_image = result['image']\n        # scaled_image will have dimensions in the range [90, 110] x [90, 110]\n        # (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_limit: ScaleFloatType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(cls, v: ScaleFloatType) -> tuple[float, float]:\n            return to_tuple(v, bias=1.0)\n\n    def __init__(\n        self,\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def get_params(self) -> dict[str, float]:\n        return {\"scale\": self.py_random.uniform(*self.scale_limit)}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(img, scale, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(mask, scale, self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"interpolation\": self.interpolation,\n            \"mask_interpolation\": self.mask_interpolation,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.Resize","title":"class Resize (height, width, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Resize the input to the given height and width.

Parameters:

Name Type Description height int

desired height of the output.

width int

desired width of the output.

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class Resize(DualTransform):\n    \"\"\"Resize the input to the given height and width.\n\n    Args:\n        height (int): desired height of the output.\n        width (int): desired width of the output.\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: int = Field(ge=1)\n        width: int = Field(ge=1)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(img, (self.height, self.width), interpolation=self.interpolation)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(mask, (self.height, self.width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        height, width = params[\"shape\"][:2]\n        scale_x = self.width / width\n        scale_y = self.height / height\n        return fgeometric.keypoints_scale(keypoints, scale_x, scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.resize.SmallestMaxSize","title":"class SmallestMaxSize [view source on GitHub]","text":"

Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, list of int

Maximum size of smallest side of the image after the transformation. When using a list, max size will be randomly selected from the values in the list. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must be at least these values - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the smallest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be larger than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / min(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = max(H_target/H, W_target/W)\n       This ensures both dimensions are at least as large as specified.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Using max_size\n>>> transform1 = A.SmallestMaxSize(max_size=120)\n>>> # Input image (100, 150) -> Output (120, 180)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n>>> # Input (80, 160) -> Output (100, 200)\n>>> # Input (160, 80) -> Output (400, 200)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n>>> # Input (80, 160) -> Output (100, 200)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class SmallestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints,\n    keeping the aspect ratio.\n\n    Args:\n        max_size (int, list of int, optional): Maximum size of smallest side of the image after the transformation.\n            When using a list, max size will be randomly selected from the values in the list. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must be at least these values\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the smallest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be larger than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / min(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = max(H_target/H, W_target/W)\n               This ensures both dimensions are at least as large as specified.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Using max_size\n        >>> transform1 = A.SmallestMaxSize(max_size=120)\n        >>> # Input image (100, 150) -> Output (120, 180)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n        >>> # Input (80, 160) -> Output (100, 200)\n        >>> # Input (160, 80) -> Output (400, 200)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n        >>> # Input (80, 160) -> Output (100, 200)\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / min(img_h, img_w)\n        elif self.max_size_hw is not None:\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on smallest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = max(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate","title":"rotate","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.RandomRotate90","title":"class RandomRotate90 [view source on GitHub]","text":"

Randomly rotate the input by 90 degrees zero or more times.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RandomRotate90(DualTransform):\n    \"\"\"Randomly rotate the input by 90 degrees zero or more times.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, factor: Literal[0, 1, 2, 3], **params: Any) -> np.ndarray:\n        return fgeometric.rot90(img, factor)\n\n    def get_params(self) -> dict[str, int]:\n        # Random int in the range [0, 3]\n        return {\"factor\": self.py_random.randint(0, 3)}\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        factor: Literal[0, 1, 2, 3],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_rot90(bboxes, factor)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        factor: Literal[0, 1, 2, 3],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_rot90(keypoints, factor, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.Rotate","title":"class Rotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', crop_border=False, mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input by an angle selected randomly from the uniform distribution.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method str

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

crop_border bool

Whether to crop border after rotation. If True, the output image size might differ from the input. Default: False

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation angle is randomly selected for each execution within the range specified by 'limit'.
  • When 'crop_border' is False, the output image will have the same size as the input, potentially introducing black triangles in the corners.
  • When 'crop_border' is True, the output image is cropped to remove black triangles, which may result in a smaller image.
  • Bounding boxes are rotated and may change size or shape.
  • Keypoints are rotated around the center of the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. Each point (x, y) in the image is transformed to (x', y') by: [x'] cos(\u03b8) -sin(\u03b8) [cx] [y'] = sin(\u03b8) cos(\u03b8) + [cy] where (cx, cy) is the center of the image. 5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Rotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class Rotate(DualTransform):\n    \"\"\"Rotate the input by an angle selected randomly from the uniform distribution.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        rotate_method (str): Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'.\n            Default: 'largest_box'\n        crop_border (bool): Whether to crop border after rotation. If True, the output image size might differ\n            from the input. Default: False\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation angle is randomly selected for each execution within the range specified by 'limit'.\n        - When 'crop_border' is False, the output image will have the same size as the input, potentially\n          introducing black triangles in the corners.\n        - When 'crop_border' is True, the output image is cropped to remove black triangles, which may result\n          in a smaller image.\n        - Bounding boxes are rotated and may change size or shape.\n        - Keypoints are rotated around the center of the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [cos(\u03b8)  -sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [sin(\u03b8)   cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n        5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Rotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        crop_border: bool\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_value(self) -> Self:\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        crop_border: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.crop_border = crop_border\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            img,\n            matrix,\n            self.interpolation,\n            self.fill,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            mask,\n            matrix,\n            self.mask_interpolation,\n            self.fill_mask,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_out = fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            image_shape,\n            self.border_mode,\n            image_shape,\n        )\n        if self.crop_border:\n            return fcrops.crop_bboxes_by_coords(\n                bboxes_out,\n                (x_min, y_min, x_max, y_max),\n                image_shape,\n            )\n        return bboxes_out\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints_out = fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"][:2],\n            scale={\"x\": 1, \"y\": 1},\n            border_mode=self.border_mode,\n        )\n        if self.crop_border:\n            return fcrops.crop_keypoints_by_coords(\n                keypoints_out,\n                (x_min, y_min, x_max, y_max),\n            )\n        return keypoints_out\n\n    @staticmethod\n    def _rotated_rect_with_max_area(\n        height: int,\n        width: int,\n        angle: float,\n    ) -> dict[str, int]:\n        \"\"\"Given a rectangle of size wxh that has been rotated by 'angle' (in\n        degrees), computes the width and height of the largest possible\n        axis-aligned rectangle (maximal area) within the rotated rectangle.\n\n        Reference:\n            https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders\n        \"\"\"\n        angle = math.radians(angle)\n        width_is_longer = width >= height\n        side_long, side_short = (width, height) if width_is_longer else (height, width)\n\n        # since the solutions for angle, -angle and 180-angle are all the same,\n        # it is sufficient to look at the first quadrant and the absolute values of sin,cos:\n        sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))\n        if side_short <= 2.0 * sin_a * cos_a * side_long or abs(sin_a - cos_a) < SMALL_NUMBER:\n            # half constrained case: two crop corners touch the longer side,\n            # the other two corners are on the mid-line parallel to the longer line\n            x = 0.5 * side_short\n            wr, hr = (x / sin_a, x / cos_a) if width_is_longer else (x / cos_a, x / sin_a)\n        else:\n            # fully constrained case: crop touches all 4 sides\n            cos_2a = cos_a * cos_a - sin_a * sin_a\n            wr, hr = (\n                (width * cos_a - height * sin_a) / cos_2a,\n                (height * cos_a - width * sin_a) / cos_2a,\n            )\n\n        return {\n            \"x_min\": max(0, int(width / 2 - wr / 2)),\n            \"x_max\": min(width, int(width / 2 + wr / 2)),\n            \"y_min\": max(0, int(height / 2 - hr / 2)),\n            \"y_max\": min(height, int(height / 2 + hr / 2)),\n        }\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        angle = self.py_random.uniform(*self.limit)\n\n        if self.crop_border:\n            height, width = params[\"shape\"][:2]\n            out_params = self._rotated_rect_with_max_area(height, width, angle)\n        else:\n            out_params = {\"x_min\": -1, \"x_max\": -1, \"y_min\": -1, \"y_max\": -1}\n\n        center = fgeometric.center(params[\"shape\"][:2])\n        bbox_center = fgeometric.center_bbox(params[\"shape\"][:2])\n\n        translate: fgeometric.XYInt = {\"x\": 0, \"y\": 0}\n        shear: fgeometric.XYFloat = {\"x\": 0, \"y\": 0}\n        scale: fgeometric.XYFloat = {\"x\": 1, \"y\": 1}\n        rotate = angle\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            center,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_center,\n        )\n        out_params[\"matrix\"] = matrix\n        out_params[\"bbox_matrix\"] = bbox_matrix\n\n        return out_params\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"crop_border\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.RotateInitSchema","title":"class RotateInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RotateInitSchema(BaseTransformInitSchema):\n    limit: SymmetricRangeType\n\n    interpolation: InterpolationType\n    mask_interpolation: InterpolationType\n\n    border_mode: BorderModeType\n\n    fill: ColorType | None\n    fill_mask: ColorType | None\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.rotate.SafeRotate","title":"class SafeRotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.

This transformation ensures that the entire rotated image fits within the original frame by scaling it down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the rotation and scaling process.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method Literal[\"largest_box\", \"ellipse\"]

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation is performed around the center of the image.
  • After rotation, the image is scaled to fit within the original frame, which may cause some distortion.
  • The output image will always have the same dimensions as the input image.
  • Bounding boxes and keypoints are transformed along with the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. The scaling factor s is calculated to ensure the rotated image fits within the original frame: s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|), height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|)) 5. The combined transformation matrix T is: T = [scos(\u03b8) -ssin(\u03b8) tx] [ssin(\u03b8) scos(\u03b8) ty] where tx and ty are translation factors to keep the image centered. 6. Each point (x, y) in the image is transformed to (x', y') by: [x'] scos(\u03b8) ssin(\u03b8) [cx] [y'] = -ssin(\u03b8) scos(\u03b8) + [cy] where (cx, cy) is the center of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.SafeRotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n# scaled to fit within the original 100x100 frame\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class SafeRotate(Affine):\n    \"\"\"Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.\n\n    This transformation ensures that the entire rotated image fits within the original frame by scaling it\n    down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the\n    rotation and scaling process.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied\n            for masks.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): Method to rotate bounding boxes.\n            Should be 'largest_box' or 'ellipse'. Default: 'largest_box'\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation is performed around the center of the image.\n        - After rotation, the image is scaled to fit within the original frame, which may cause some distortion.\n        - The output image will always have the same dimensions as the input image.\n        - Bounding boxes and keypoints are transformed along with the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. The scaling factor s is calculated to ensure the rotated image fits within the original frame:\n           s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|),\n                   height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|))\n        5. The combined transformation matrix T is:\n           T = [s*cos(\u03b8)  -s*sin(\u03b8)  tx]\n               [s*sin(\u03b8)   s*cos(\u03b8)  ty]\n           where tx and ty are translation factors to keep the image centered.\n        6. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [s*cos(\u03b8)   s*sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [-s*sin(\u03b8)  s*cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.SafeRotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n        # scaled to fit within the original 100x100 frame\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            rotate=limit,\n            interpolation=interpolation,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            rotate_method=rotate_method,\n            fit_output=True,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.mask_interpolation = mask_interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"mask_interpolation\",\n        )\n\n    def _create_safe_rotate_matrix(\n        self,\n        angle: float,\n        center: tuple[float, float],\n        image_shape: tuple[int, int],\n    ) -> tuple[np.ndarray, dict[str, float]]:\n        height, width = image_shape[:2]\n        rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.0)\n\n        # Calculate new image size\n        abs_cos = abs(rotation_mat[0, 0])\n        abs_sin = abs(rotation_mat[0, 1])\n        new_w = int(height * abs_sin + width * abs_cos)\n        new_h = int(height * abs_cos + width * abs_sin)\n\n        # Adjust the rotation matrix to take into account the new size\n        rotation_mat[0, 2] += new_w / 2 - center[0]\n        rotation_mat[1, 2] += new_h / 2 - center[1]\n\n        # Calculate scaling factors\n        scale_x = width / new_w\n        scale_y = height / new_h\n\n        # Create scaling matrix\n        scale_mat = np.array([[scale_x, 0, 0], [0, scale_y, 0], [0, 0, 1]])\n\n        # Combine rotation and scaling\n        matrix = scale_mat @ np.vstack([rotation_mat, [0, 0, 1]])\n\n        return matrix, {\"x\": scale_x, \"y\": scale_y}\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        angle = self.py_random.uniform(*self.limit)\n\n        # Calculate centers for image and bbox\n        image_center = fgeometric.center(image_shape)\n        bbox_center = fgeometric.center_bbox(image_shape)\n\n        # Create matrices for image and bbox\n        matrix, scale = self._create_safe_rotate_matrix(\n            angle,\n            image_center,\n            image_shape,\n        )\n        bbox_matrix, _ = self._create_safe_rotate_matrix(\n            angle,\n            bbox_center,\n            image_shape,\n        )\n\n        return {\n            \"rotate\": angle,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": image_shape,\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms","title":"transforms","text":""},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Affine","title":"class Affine (scale=1, translate_percent=None, translate_px=None, rotate=0, shear=0, interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, fit_output=False, keep_ratio=False, rotate_method='largest_box', balanced_scale=False, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Augmentation to apply affine transformations to images.

Affine transformations involve:

- Translation (\"move\" image on the x-/y-axis)\n- Rotation\n- Scaling (\"zoom\" in/out)\n- Shear (move one side of the image, turning a square into a trapezoid)\n

All such transformations can create \"new\" pixels in the image without a defined content, e.g. if the image is translated to the left, pixels are created on the right. A method has to be defined to deal with these pixel values. The parameters fill and fill_mask of this class deal with this.

Some transformations involve interpolations between several pixels of the input image to generate output pixel values. The parameters interpolation and mask_interpolation deals with the method of interpolation used for this.

Parameters:

Name Type Description scale number, tuple of number or dict

Scaling factor to use, where 1.0 denotes \"no change\" and 0.5 is zoomed out to 50 percent of the original size. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That the same range will be used for both x- and y-axis. To keep the aspect ratio, set keep_ratio=True, then the same value will be used for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes. Note that when the keep_ratio=True, the x- and y-axis ranges should be the same.

translate_percent None, number, tuple of number or dict

Translation as a fraction of the image height/width (x-translation, y-translation), where 0 denotes \"no change\" and 0.5 denotes \"half of the axis size\". * If None then equivalent to 0.0 unless translate_px has a value other than None. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That sampled fraction value will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

translate_px None, int, tuple of int or dict

Translation in pixels. * If None then equivalent to 0 unless translate_percent has a value other than None. * If a single int, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the discrete interval [a..b]. That number will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

rotate number or tuple of number

Rotation in degrees (NOT radians), i.e. expected value range is around [-360, 360]. Rotation happens around the center of the image, not the top left corner as in some other frameworks. * If a number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b] and used as the rotation value.

shear number, tuple of number or dict

Shear in degrees (NOT radians), i.e. expected value range is around [-360, 360], with reasonable values being in the range of [-45, 45]. * If a number, then that value will be used for all images as the shear on the x-axis (no shear on the y-axis will be done). * If a tuple (a, b), then two value will be uniformly sampled per image from the interval [a, b] and be used as the x- and y-shear value. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

interpolation int

OpenCV interpolation flag.

mask_interpolation int

OpenCV interpolation flag.

fill ColorType

The constant value to use when filling in newly created pixels. (E.g. translating by 1px to the right will create a new 1px-wide column of pixels on the left of the image). The value is only used when mode=constant. The expected value range is [0, 255] for uint8 images.

fill_mask ColorType

Same as fill but only for masks.

border_mode int

OpenCV border flag.

fit_output bool

If True, the image plane size and position will be adjusted to tightly capture the whole image after affine transformation (translate_percent and translate_px are ignored). Otherwise (False), parts of the transformed image may end up outside the image plane. Fitting the output shape can be useful to avoid corners of the image being outside the image plane after applying rotations. Default: False

keep_ratio bool

When True, the original aspect ratio will be kept when the random scale is applied. Default: False.

rotate_method Literal[\"largest_box\", \"ellipse\"]

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"

balanced_scale bool

When True, scaling factors are chosen to be either entirely below or above 1, ensuring balanced scaling. Default: False.

This is important because without it, scaling tends to lean towards upscaling. For example, if we want the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly from [0.5, 2]. With balanced_scale, the function ensures that half the time, the scaling factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in). This makes the zooming in and out process more balanced.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

[1] https://arxiv.org/abs/2109.13488

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Affine(DualTransform):\n    \"\"\"Augmentation to apply affine transformations to images.\n\n    Affine transformations involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a defined content, e.g.\n    if the image is translated to the left, pixels are created on the right.\n    A method has to be defined to deal with these pixel values.\n    The parameters `fill` and `fill_mask` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameters `interpolation` and\n    `mask_interpolation` deals with the method of interpolation used for this.\n\n    Args:\n        scale (number, tuple of number or dict): Scaling factor to use, where ``1.0`` denotes \"no change\" and\n            ``0.5`` is zoomed out to ``50`` percent of the original size.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That the same range will be used for both x- and y-axis. To keep the aspect ratio, set\n                  ``keep_ratio=True``, then the same value will be used for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes. Note that when\n                  the ``keep_ratio=True``, the x- and y-axis ranges should be the same.\n        translate_percent (None, number, tuple of number or dict): Translation as a fraction of the image height/width\n            (x-translation, y-translation), where ``0`` denotes \"no change\"\n            and ``0.5`` denotes \"half of the axis size\".\n                * If ``None`` then equivalent to ``0.0`` unless `translate_px` has a value other than ``None``.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That sampled fraction value will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        translate_px (None, int, tuple of int or dict): Translation in pixels.\n                * If ``None`` then equivalent to ``0`` unless `translate_percent` has a value other than ``None``.\n                * If a single int, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from\n                  the discrete interval ``[a..b]``. That number will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        rotate (number or tuple of number): Rotation in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``. Rotation happens around the *center* of the image,\n            not the top left corner as in some other frameworks.\n                * If a number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``\n                  and used as the rotation value.\n        shear (number, tuple of number or dict): Shear in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``, with reasonable values being in the range of ``[-45, 45]``.\n                * If a number, then that value will be used for all images as\n                  the shear on the x-axis (no shear on the y-axis will be done).\n                * If a tuple ``(a, b)``, then two value will be uniformly sampled per image\n                  from the interval ``[a, b]`` and be used as the x- and y-shear value.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        interpolation (int): OpenCV interpolation flag.\n        mask_interpolation (int): OpenCV interpolation flag.\n        fill (ColorType): The constant value to use when filling in newly created pixels.\n            (E.g. translating by 1px to the right will create a new 1px-wide column of pixels\n            on the left of the image).\n            The value is only used when `mode=constant`. The expected value range is ``[0, 255]`` for ``uint8`` images.\n        fill_mask (ColorType): Same as fill but only for masks.\n        border_mode (int): OpenCV border flag.\n        fit_output (bool): If True, the image plane size and position will be adjusted to tightly capture\n            the whole image after affine transformation (`translate_percent` and `translate_px` are ignored).\n            Otherwise (``False``),  parts of the transformed image may end up outside the image plane.\n            Fitting the output shape can be useful to avoid corners of the image being outside the image plane\n            after applying rotations. Default: False\n        keep_ratio (bool): When True, the original aspect ratio will be kept when the random scale is applied.\n            Default: False.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): rotation method used for the bounding boxes.\n            Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"\n        balanced_scale (bool): When True, scaling factors are chosen to be either entirely below or above 1,\n            ensuring balanced scaling. Default: False.\n\n            This is important because without it, scaling tends to lean towards upscaling. For example, if we want\n            the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is\n            three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly\n            from [0.5, 2]. With `balanced_scale`, the  function ensures that half the time, the scaling\n            factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in).\n            This makes the zooming in and out process more balanced.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        [1] https://arxiv.org/abs/2109.13488\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: ScaleFloatType | fgeometric.XYFloatScale\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None\n        rotate: ScaleFloatType\n        shear: ScaleFloatType | fgeometric.XYFloatScale\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        cval: ColorType | None\n        cval_mask: ColorType | None\n        mode: BorderModeType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        fit_output: bool\n        keep_ratio: bool\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        balanced_scale: bool\n\n        @field_validator(\"shear\", \"scale\")\n        @classmethod\n        def process_shear(\n            cls,\n            value: ScaleFloatType | fgeometric.XYFloatScale,\n            info: ValidationInfo,\n        ) -> fgeometric.XYFloatDict:\n            return cast(\n                fgeometric.XYFloatDict,\n                cls._handle_dict_arg(value, info.field_name),\n            )\n\n        @field_validator(\"rotate\")\n        @classmethod\n        def process_rotate(\n            cls,\n            value: ScaleFloatType,\n        ) -> tuple[float, float]:\n            return to_tuple(value, value)\n\n        @model_validator(mode=\"after\")\n        def handle_translate(self) -> Self:\n            if self.translate_percent is None and self.translate_px is None:\n                self.translate_px = 0\n\n            if self.translate_percent is not None and self.translate_px is not None:\n                msg = \"Expected either translate_percent or translate_px to be provided, but both were provided.\"\n                raise ValueError(msg)\n\n            if self.translate_percent is not None:\n                self.translate_percent = self._handle_dict_arg(\n                    self.translate_percent,\n                    \"translate_percent\",\n                    default=0.0,\n                )  # type: ignore[assignment]\n\n            if self.translate_px is not None:\n                self.translate_px = self._handle_dict_arg(\n                    self.translate_px,\n                    \"translate_px\",\n                    default=0,\n                )  # type: ignore[assignment]\n\n            return self\n\n        @staticmethod\n        def _handle_dict_arg(\n            val: ScaleType | fgeometric.XYFloatScale | fgeometric.XYIntScale,\n            name: str | None,\n            default: float = 1.0,\n        ) -> dict[str, Any]:\n            if isinstance(val, dict):\n                if \"x\" not in val and \"y\" not in val:\n                    raise ValueError(\n                        f'Expected {name} dictionary to contain at least key \"x\" or key \"y\". Found neither of them.',\n                    )\n                x = val.get(\"x\", default)\n                y = val.get(\"y\", default)\n                return {\"x\": to_tuple(x, x), \"y\": to_tuple(y, y)}  # type: ignore[arg-type]\n            return {\"x\": to_tuple(val, val), \"y\": to_tuple(val, val)}\n\n        @model_validator(mode=\"after\")\n        def validate_fill_types(self) -> Self:\n            if self.cval is not None:\n                self.fill = self.cval\n                warn(\"cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n            if self.cval_mask is not None:\n                self.fill_mask = self.cval_mask\n                warn(\"cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n            if self.mode is not None:\n                self.border_mode = self.mode\n                warn(\"mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType | fgeometric.XYFloatScale = 1,\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None = None,\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None = None,\n        rotate: ScaleFloatType = 0,\n        shear: ScaleFloatType | fgeometric.XYFloatScale = 0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: ColorType | None = None,\n        cval_mask: ColorType | None = None,\n        mode: int | None = None,\n        fit_output: bool = False,\n        keep_ratio: bool = False,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        balanced_scale: bool = False,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n        self.scale = cast(fgeometric.XYFloatDict, scale)\n        self.translate_percent = cast(fgeometric.XYFloatDict, translate_percent)\n        self.translate_px = cast(fgeometric.XYIntDict, translate_px)\n        self.rotate = cast(tuple[float, float], rotate)\n        self.fit_output = fit_output\n        self.shear = cast(fgeometric.XYFloatDict, shear)\n        self.keep_ratio = keep_ratio\n        self.rotate_method = rotate_method\n        self.balanced_scale = balanced_scale\n\n        if self.keep_ratio and self.scale[\"x\"] != self.scale[\"y\"]:\n            raise ValueError(\n                f\"When keep_ratio is True, the x and y scale range should be identical. got {self.scale}\",\n            )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"fill\",\n            \"border_mode\",\n            \"scale\",\n            \"translate_percent\",\n            \"translate_px\",\n            \"rotate\",\n            \"fit_output\",\n            \"shear\",\n            \"fill_mask\",\n            \"keep_ratio\",\n            \"rotate_method\",\n            \"balanced_scale\",\n        )\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            img,\n            matrix,\n            interpolation=self.interpolation,\n            fill=self.fill,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            mask,\n            matrix,\n            interpolation=self.mask_interpolation,\n            fill=self.fill_mask,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            params[\"shape\"][:2],\n            self.border_mode,\n            output_shape,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        scale: fgeometric.XYFloat,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"],\n            scale,\n            self.border_mode,\n        )\n\n    @staticmethod\n    def get_scale(\n        scale: fgeometric.XYFloatDict,\n        keep_ratio: bool,\n        balanced_scale: bool,\n        random_state: random.Random,\n    ) -> fgeometric.XYFloat:\n        result_scale = {}\n        for key, value in scale.items():\n            if isinstance(value, (int, float)):\n                result_scale[key] = float(value)\n            elif isinstance(value, tuple):\n                if balanced_scale:\n                    lower_interval = (value[0], 1.0) if value[0] < 1 else None\n                    upper_interval = (1.0, value[1]) if value[1] > 1 else None\n\n                    if lower_interval is not None and upper_interval is not None:\n                        selected_interval = random_state.choice(\n                            [lower_interval, upper_interval],\n                        )\n                    elif lower_interval is not None:\n                        selected_interval = lower_interval\n                    elif upper_interval is not None:\n                        selected_interval = upper_interval\n                    else:\n                        result_scale[key] = 1.0\n                        continue\n\n                    result_scale[key] = random_state.uniform(*selected_interval)\n                else:\n                    result_scale[key] = random_state.uniform(*value)\n            else:\n                raise TypeError(\n                    f\"Invalid scale value for key {key}: {value}. Expected a float or a tuple of two floats.\",\n                )\n\n        if keep_ratio:\n            result_scale[\"y\"] = result_scale[\"x\"]\n\n        return cast(fgeometric.XYFloat, result_scale)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        translate = self._get_translate_params(image_shape)\n        shear = self._get_shear_params()\n        scale = self.get_scale(\n            self.scale,\n            self.keep_ratio,\n            self.balanced_scale,\n            self.py_random,\n        )\n        rotate = self.py_random.uniform(*self.rotate)\n\n        image_shift = fgeometric.center(image_shape)\n        bbox_shift = fgeometric.center_bbox(image_shape)\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            image_shift,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_shift,\n        )\n\n        if self.fit_output:\n            matrix, output_shape = fgeometric.compute_affine_warp_output_shape(\n                matrix,\n                image_shape,\n            )\n            bbox_matrix, _ = fgeometric.compute_affine_warp_output_shape(\n                bbox_matrix,\n                image_shape,\n            )\n        else:\n            output_shape = image_shape\n\n        return {\n            \"rotate\": rotate,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": output_shape,\n        }\n\n    def _get_translate_params(self, image_shape: tuple[int, int]) -> fgeometric.XYInt:\n        height, width = image_shape[:2]\n        if self.translate_px is not None:\n            return {\n                \"x\": self.py_random.randint(*self.translate_px[\"x\"]),\n                \"y\": self.py_random.randint(*self.translate_px[\"y\"]),\n            }\n        if self.translate_percent is not None:\n            translate = {key: self.py_random.uniform(*value) for key, value in self.translate_percent.items()}\n            return cast(\n                fgeometric.XYInt,\n                {\"x\": int(translate[\"x\"] * width), \"y\": int(translate[\"y\"] * height)},\n            )\n        return cast(fgeometric.XYInt, {\"x\": 0, \"y\": 0})\n\n    def _get_shear_params(self) -> fgeometric.XYFloat:\n        return {\n            \"x\": -self.py_random.uniform(*self.shear[\"x\"]),\n            \"y\": -self.py_random.uniform(*self.shear[\"y\"]),\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.BaseDistortion","title":"class BaseDistortion (interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Base class for distortion-based transformations.

This class provides a foundation for implementing various types of image distortions, such as optical distortions, grid distortions, and elastic transformations. It handles the common operations of applying distortions to images, masks, bounding boxes, and keypoints.

Parameters:

Name Type Description interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR, cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This is an abstract base class and should not be used directly.
  • Subclasses should implement the get_params_dependent_on_data method to generate the distortion maps (map_x and map_y).
  • The distortion is applied consistently across all targets (image, mask, bboxes, keypoints) to maintain coherence in the augmented data.

Example of a subclass: class CustomDistortion(BaseDistortion): def init(self, args, **kwargs): super().init(args, **kwargs) # Add custom parameters here

    def get_params_dependent_on_data(self, params, data):\n        # Generate and return map_x and map_y based on the distortion logic\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self):\n        return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class BaseDistortion(DualTransform):\n    \"\"\"Base class for distortion-based transformations.\n\n    This class provides a foundation for implementing various types of image distortions,\n    such as optical distortions, grid distortions, and elastic transformations. It handles\n    the common operations of applying distortions to images, masks, bounding boxes, and keypoints.\n\n    Args:\n        interpolation (int): Interpolation method to be used for image transformation.\n            Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR,\n            cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This is an abstract base class and should not be used directly.\n        - Subclasses should implement the `get_params_dependent_on_data` method to generate\n          the distortion maps (map_x and map_y).\n        - The distortion is applied consistently across all targets (image, mask, bboxes, keypoints)\n          to maintain coherence in the augmented data.\n\n    Example of a subclass:\n        class CustomDistortion(BaseDistortion):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                # Add custom parameters here\n\n            def get_params_dependent_on_data(self, params, data):\n                # Generate and return map_x and map_y based on the distortion logic\n                return {\"map_x\": map_x, \"map_y\": map_y}\n\n            def get_transform_init_args_names(self):\n                return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            img,\n            map_x,\n            map_y,\n            self.interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            mask,\n            map_x,\n            map_y,\n            self.mask_interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        bboxes_returned = fgeometric.remap_bboxes(\n            bboxes_denorm,\n            map_x,\n            map_y,\n            image_shape,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap_keypoints(keypoints, map_x, map_y, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.D4","title":"class D4 (p=1, always_apply=None) [view source on GitHub]","text":"

Applies one of the eight possible D4 dihedral group transformations to a square-shaped input, maintaining the square shape. These transformations correspond to the symmetries of a square, including rotations and reflections.

The D4 group transformations include: - 'e' (identity): No transformation is applied. - 'r90' (rotation by 90 degrees counterclockwise) - 'r180' (rotation by 180 degrees) - 'r270' (rotation by 270 degrees counterclockwise) - 'v' (reflection across the vertical midline) - 'hvt' (reflection across the anti-diagonal) - 'h' (reflection across the horizontal midline) - 't' (reflection across the main diagonal)

Even if the probability (p) of applying the transform is set to 1, the identity transformation 'e' may still occur, which means the input will remain unchanged in one out of eight cases.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for augmenting data that does not have a clear orientation, such as top-view satellite or drone imagery, or certain types of medical images.
  • The input image should be square-shaped for optimal results. Non-square inputs may lead to unexpected behavior or distortions.
  • When applied to bounding boxes or keypoints, their coordinates will be adjusted according to the selected transformation.
  • This transform preserves the aspect ratio and size of the input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.D4(p=1.0),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed['image']\n# The resulting image will be one of the 8 possible D4 transformations of the input\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class D4(DualTransform):\n    \"\"\"Applies one of the eight possible D4 dihedral group transformations to a square-shaped input,\n    maintaining the square shape. These transformations correspond to the symmetries of a square,\n    including rotations and reflections.\n\n    The D4 group transformations include:\n    - 'e' (identity): No transformation is applied.\n    - 'r90' (rotation by 90 degrees counterclockwise)\n    - 'r180' (rotation by 180 degrees)\n    - 'r270' (rotation by 270 degrees counterclockwise)\n    - 'v' (reflection across the vertical midline)\n    - 'hvt' (reflection across the anti-diagonal)\n    - 'h' (reflection across the horizontal midline)\n    - 't' (reflection across the main diagonal)\n\n    Even if the probability (`p`) of applying the transform is set to 1, the identity transformation\n    'e' may still occur, which means the input will remain unchanged in one out of eight cases.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for augmenting data that does not have a clear orientation,\n          such as top-view satellite or drone imagery, or certain types of medical images.\n        - The input image should be square-shaped for optimal results. Non-square inputs may lead to\n          unexpected behavior or distortions.\n        - When applied to bounding boxes or keypoints, their coordinates will be adjusted according\n          to the selected transformation.\n        - This transform preserves the aspect ratio and size of the input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.D4(p=1.0),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed['image']\n        # The resulting image will be one of the 8 possible D4 transformations of the input\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.d4(img, group_element)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_d4(bboxes, group_element)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_d4(keypoints, group_element, params[\"shape\"])\n\n    def get_params(self) -> dict[str, D4Type]:\n        return {\n            \"group_element\": self.random_generator.choice(d4_group_elements),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ElasticTransform","title":"class ElasticTransform (alpha=1, sigma=50, interpolation=1, border_mode=4, value=None, mask_value=None, approximate=False, same_dxdy=False, mask_interpolation=0, noise_distribution='gaussian', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformation to images, masks, bounding boxes, and keypoints.

This transformation introduces random elastic distortions to the input data. It's particularly useful for data augmentation in training deep learning models, especially for tasks like image segmentation or object detection where you want to maintain the relative positions of features while introducing realistic deformations.

The transform works by generating random displacement fields and applying them to the input. These fields are smoothed using a Gaussian filter to create more natural-looking distortions.

Parameters:

Name Type Description alpha float

Scaling factor for the random displacement fields. Higher values result in more pronounced distortions. Default: 1.0

sigma float

Standard deviation of the Gaussian filter used to smooth the displacement fields. Higher values result in smoother, more global distortions. Default: 50.0

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

approximate bool

Whether to use an approximate version of the elastic transform. If True, uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially less accurate for large sigma values. Default: False

same_dxdy bool

Whether to use the same random displacement field for both x and y directions. Can speed up the transform at the cost of less diverse distortions. Default: False

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

noise_distribution Literal[\"gaussian\", \"uniform\"]

Distribution used to generate the displacement fields. \"gaussian\" generates fields using normal distribution (more natural deformations). \"uniform\" generates fields using uniform distribution (more mechanical deformations). Default: \"gaussian\".

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints) by using the same displacement fields for all.
  • The 'approximate' parameter determines whether to use a precise or approximate method for generating displacement fields. The approximate method can be faster but may be less accurate for large sigma values.
  • Bounding boxes that end up outside the image after transformation will be removed.
  • Keypoints that end up outside the image after transformation will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ElasticTransform(BaseDistortion):\n    \"\"\"Apply elastic deformation to images, masks, bounding boxes, and keypoints.\n\n    This transformation introduces random elastic distortions to the input data. It's particularly\n    useful for data augmentation in training deep learning models, especially for tasks like\n    image segmentation or object detection where you want to maintain the relative positions of\n    features while introducing realistic deformations.\n\n    The transform works by generating random displacement fields and applying them to the input.\n    These fields are smoothed using a Gaussian filter to create more natural-looking distortions.\n\n    Args:\n        alpha (float): Scaling factor for the random displacement fields. Higher values result in\n            more pronounced distortions. Default: 1.0\n        sigma (float): Standard deviation of the Gaussian filter used to smooth the displacement\n            fields. Higher values result in smoother, more global distortions. Default: 50.0\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        approximate (bool): Whether to use an approximate version of the elastic transform. If True,\n            uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially\n            less accurate for large sigma values. Default: False\n        same_dxdy (bool): Whether to use the same random displacement field for both x and y\n            directions. Can speed up the transform at the cost of less diverse distortions. Default: False\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        noise_distribution (Literal[\"gaussian\", \"uniform\"]): Distribution used to generate the displacement fields.\n            \"gaussian\" generates fields using normal distribution (more natural deformations).\n            \"uniform\" generates fields using uniform distribution (more mechanical deformations).\n            Default: \"gaussian\".\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints)\n          by using the same displacement fields for all.\n        - The 'approximate' parameter determines whether to use a precise or approximate method for\n          generating displacement fields. The approximate method can be faster but may be less\n          accurate for large sigma values.\n        - Bounding boxes that end up outside the image after transformation will be removed.\n        - Keypoints that end up outside the image after transformation will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        alpha: Annotated[float, Field(ge=0)]\n        sigma: Annotated[float, Field(ge=1)]\n        approximate: bool\n        same_dxdy: bool\n        noise_distribution: Literal[\"gaussian\", \"uniform\"]\n        border_mode: BorderModeType = Field(deprecated=\"Deprecated\")\n        value: ColorType | None = Field(deprecated=\"Deprecated\")\n        mask_value: ColorType | None = Field(deprecated=\"Deprecated\")\n\n    def __init__(\n        self,\n        alpha: float = 1,\n        sigma: float = 50,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        approximate: bool = False,\n        same_dxdy: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        noise_distribution: Literal[\"gaussian\", \"uniform\"] = \"gaussian\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.alpha = alpha\n        self.sigma = sigma\n        self.approximate = approximate\n        self.same_dxdy = same_dxdy\n        self.noise_distribution = noise_distribution\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        kernel_size = (0, 0) if self.approximate else (17, 17)\n\n        # Generate displacement fields\n        dx, dy = fgeometric.generate_displacement_fields(\n            (height, width),\n            self.alpha,\n            self.sigma,\n            same_dxdy=self.same_dxdy,\n            kernel_size=kernel_size,\n            random_generator=self.random_generator,\n            noise_distribution=self.noise_distribution,\n        )\n\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        map_x = np.float32(x + dx)\n        map_y = np.float32(y + dy)\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"alpha\",\n            \"sigma\",\n            \"approximate\",\n            \"same_dxdy\",\n            \"noise_distribution\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.GridDistortion","title":"class GridDistortion (num_steps=5, distort_limit=(-0.3, 0.3), interpolation=1, border_mode=4, value=None, mask_value=None, normalized=True, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply grid distortion to images, masks, bounding boxes, and keypoints.

This transformation divides the image into a grid and randomly distorts each cell, creating localized warping effects. It's particularly useful for data augmentation in tasks like medical image analysis, OCR, and other domains where local geometric variations are meaningful.

Parameters:

Name Type Description num_steps int

Number of grid cells on each side of the image. Higher values create more granular distortions. Must be at least 1. Default: 5.

distort_limit float or tuple[float, float]

Range of distortion. If a single float is provided, the range will be (-distort_limit, distort_limit). Higher values create stronger distortions. Should be in the range of -1 to 1. Default: (-0.3, 0.3).

interpolation int

OpenCV interpolation method used for image transformation. Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.

normalized bool

If True, ensures that the distortion does not move pixels outside the image boundaries. This can result in less extreme distortions but guarantees that no information is lost. Default: True.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The same distortion is applied to all targets (image, mask, bboxes, keypoints) to maintain consistency.
  • When normalized=True, the distortion is adjusted to ensure all pixels remain within the image boundaries.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridDistortion(BaseDistortion):\n    \"\"\"Apply grid distortion to images, masks, bounding boxes, and keypoints.\n\n    This transformation divides the image into a grid and randomly distorts each cell,\n    creating localized warping effects. It's particularly useful for data augmentation\n    in tasks like medical image analysis, OCR, and other domains where local geometric\n    variations are meaningful.\n\n    Args:\n        num_steps (int): Number of grid cells on each side of the image. Higher values\n            create more granular distortions. Must be at least 1. Default: 5.\n        distort_limit (float or tuple[float, float]): Range of distortion. If a single float\n            is provided, the range will be (-distort_limit, distort_limit). Higher values\n            create stronger distortions. Should be in the range of -1 to 1.\n            Default: (-0.3, 0.3).\n        interpolation (int): OpenCV interpolation method used for image transformation.\n            Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.\n        normalized (bool): If True, ensures that the distortion does not move pixels\n            outside the image boundaries. This can result in less extreme distortions\n            but guarantees that no information is lost. Default: True.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The same distortion is applied to all targets (image, mask, bboxes, keypoints)\n          to maintain consistency.\n        - When normalized=True, the distortion is adjusted to ensure all pixels remain\n          within the image boundaries.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        num_steps: Annotated[int, Field(ge=1)]\n        distort_limit: SymmetricRangeType\n        normalized: bool\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int = Field(deprecated=\"Deprecated. Does not have any effect.\")\n\n        @field_validator(\"distort_limit\")\n        @classmethod\n        def check_limits(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = -1, 1\n            result = to_tuple(v)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        num_steps: int = 5,\n        distort_limit: ScaleFloatType = (-0.3, 0.3),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        normalized: bool = True,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.num_steps = num_steps\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.normalized = normalized\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        steps_x = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n        steps_y = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n\n        if self.normalized:\n            normalized_params = fgeometric.normalize_grid_distortion_steps(\n                image_shape,\n                self.num_steps,\n                steps_x,\n                steps_y,\n            )\n            steps_x, steps_y = (\n                normalized_params[\"steps_x\"],\n                normalized_params[\"steps_y\"],\n            )\n\n        map_x, map_y = fgeometric.generate_grid(\n            image_shape,\n            steps_x,\n            steps_y,\n            self.num_steps,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"num_steps\",\n            \"distort_limit\",\n            \"normalized\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.GridElasticDeform","title":"class GridElasticDeform (num_grid_xy, magnitude, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.

This transformation overlays a grid on the input and applies random displacements to the grid points, resulting in local elastic distortions. The granularity and intensity of the distortions can be controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.

Parameters:

Name Type Description num_grid_xy tuple[int, int]

Number of grid cells along the width and height. Specified as (grid_width, grid_height). Each value must be greater than 1.

magnitude int

Maximum pixel-wise displacement for distortion. Must be greater than 0.

interpolation int

Interpolation method to be used for the image transformation. Default: cv2.INTER_LINEAR

mask_interpolation int

Interpolation method to be used for mask transformation. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> transformed_image, transformed_mask = result['image'], result['mask']\n

Note

This transformation is particularly useful for data augmentation in medical imaging and other domains where elastic deformations can simulate realistic variations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridElasticDeform(DualTransform):\n    \"\"\"Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.\n\n    This transformation overlays a grid on the input and applies random displacements to the grid points,\n    resulting in local elastic distortions. The granularity and intensity of the distortions can be\n    controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.\n\n\n    Args:\n        num_grid_xy (tuple[int, int]): Number of grid cells along the width and height.\n            Specified as (grid_width, grid_height). Each value must be greater than 1.\n        magnitude (int): Maximum pixel-wise displacement for distortion. Must be greater than 0.\n        interpolation (int): Interpolation method to be used for the image transformation.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Interpolation method to be used for mask transformation.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = result['image'], result['mask']\n\n    Note:\n        This transformation is particularly useful for data augmentation in medical imaging\n        and other domains where elastic deformations can simulate realistic variations.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        num_grid_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        magnitude: int = Field(gt=0)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        num_grid_xy: tuple[int, int],\n        magnitude: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_grid_xy = num_grid_xy\n        self.magnitude = magnitude\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    @staticmethod\n    def generate_mesh(polygons: np.ndarray, dimensions: np.ndarray) -> np.ndarray:\n        return np.hstack((dimensions.reshape(-1, 4), polygons))\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        # Replace calculate_grid_dimensions with split_uniform_grid\n        tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.num_grid_xy,\n            self.random_generator,\n        )\n\n        # Convert tiles to the format expected by generate_distorted_grid_polygons\n        dimensions = np.array(\n            [\n                [\n                    tile[1],\n                    tile[0],\n                    tile[3],\n                    tile[2],\n                ]  # Reorder to [x_min, y_min, x_max, y_max]\n                for tile in tiles\n            ],\n        ).reshape(\n            self.num_grid_xy[::-1] + (4,),\n        )  # Reshape to (grid_height, grid_width, 4)\n\n        polygons = fgeometric.generate_distorted_grid_polygons(\n            dimensions,\n            self.magnitude,\n            self.random_generator,\n        )\n\n        generated_mesh = self.generate_mesh(polygons, dimensions)\n\n        return {\"generated_mesh\": generated_mesh}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(img, generated_mesh, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(mask, generated_mesh, self.mask_interpolation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        bboxes_denorm = denormalize_bboxes(bboxes, params[\"shape\"][:2])\n        return normalize_bboxes(\n            fgeometric.bbox_distort_image(\n                bboxes_denorm,\n                generated_mesh,\n                params[\"shape\"][:2],\n            ),\n            params[\"shape\"][:2],\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image_keypoints(\n            keypoints,\n            generated_mesh,\n            params[\"shape\"][:2],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_grid_xy\", \"magnitude\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.HorizontalFlip","title":"class HorizontalFlip [view source on GitHub]","text":"

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class HorizontalFlip(DualTransform):\n    \"\"\"Flip the input horizontally around the y-axis.\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return hflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_hflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_hflip(keypoints, params[\"shape\"][1])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.OpticalDistortion","title":"class OpticalDistortion (distort_limit=(-0.05, 0.05), shift_limit=None, interpolation=1, border_mode=None, value=None, mask_value=None, mask_interpolation=0, mode='camera', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply optical distortion to images, masks, bounding boxes, and keypoints.

Supports two distortion models: 1. Camera matrix model (original): Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients

  1. Fisheye model: Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)

Parameters:

Name Type Description distort_limit float | tuple[float, float]

Range of distortion coefficient. For camera model: recommended range (-0.05, 0.05) For fisheye model: recommended range (-0.3, 0.3) Default: (-0.05, 0.05)

mode Literal['camera', 'fisheye']

Distortion model to use: - 'camera': Original camera matrix model - 'fisheye': Fisheye lens model Default: 'camera'

interpolation OpenCV flag

Interpolation method used for image transformation. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.
  • The distortion coefficient (k) is randomly sampled from the distort_limit range.
  • The image center is shifted by dx and dy, randomly sampled from the shift_limit range.
  • Bounding boxes and keypoints are transformed along with the image to maintain consistency.
  • Fisheye model directly applies radial distortion
  • Both models use shift_limit to control distortion center

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class OpticalDistortion(BaseDistortion):\n    \"\"\"Apply optical distortion to images, masks, bounding boxes, and keypoints.\n\n    Supports two distortion models:\n    1. Camera matrix model (original):\n       Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients\n\n    2. Fisheye model:\n       Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)\n\n    Args:\n        distort_limit (float | tuple[float, float]): Range of distortion coefficient.\n            For camera model: recommended range (-0.05, 0.05)\n            For fisheye model: recommended range (-0.3, 0.3)\n            Default: (-0.05, 0.05)\n\n        mode (Literal['camera', 'fisheye']): Distortion model to use:\n            - 'camera': Original camera matrix model\n            - 'fisheye': Fisheye lens model\n            Default: 'camera'\n\n        interpolation (OpenCV flag): Interpolation method used for image transformation.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC,\n            cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.\n        - The distortion coefficient (k) is randomly sampled from the distort_limit range.\n        - The image center is shifted by dx and dy, randomly sampled from the shift_limit range.\n        - Bounding boxes and keypoints are transformed along with the image to maintain consistency.\n        - Fisheye model directly applies radial distortion\n        - Both models use shift_limit to control distortion center\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        distort_limit: SymmetricRangeType\n        mode: Literal[\"camera\", \"fisheye\"]\n        shift_limit: SymmetricRangeType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n    def __init__(\n        self,\n        distort_limit: ScaleFloatType = (-0.05, 0.05),\n        shift_limit: ScaleFloatType | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int | None = None,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        mode: Literal[\"camera\", \"fisheye\"] = \"camera\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.mode = mode\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        height, width = image_shape\n\n        # Get distortion coefficient\n        k = self.py_random.uniform(*self.distort_limit)\n\n        # Calculate center shift\n        center_xy = fgeometric.center(image_shape)\n\n        # Get distortion maps based on mode\n        if self.mode == \"camera\":\n            map_x, map_y = fgeometric.get_camera_matrix_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n        else:  # fisheye\n            map_x, map_y = fgeometric.get_fisheye_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"distort_limit\",\n            \"mode\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Pad","title":"class Pad (padding=0, fill=0, fill_mask=0, border_mode=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of an image by specified number of pixels.

Parameters:

Name Type Description padding int, tuple[int, int] or tuple[int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT

border_mode OpenCV flag

OpenCV border mode

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

References

  • https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Pad(DualTransform):\n    \"\"\"Pad the sides of an image by specified number of pixels.\n\n    Args:\n        padding (int, tuple[int, int] or tuple[int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y\n            * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT\n        fill_mask (ColorType): Padding value for mask if border_mode is cv2.BORDER_CONSTANT\n        border_mode (OpenCV flag): OpenCV border mode\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    References:\n        - https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        padding: int | tuple[int, int] | tuple[int, int, int, int]\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int] | tuple[int, int, int, int] = 0,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            img,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            mask,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill_mask,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_np = denormalize_bboxes(bboxes, params[\"shape\"])\n\n        result = fgeometric.pad_bboxes(\n            bboxes_np,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=image_shape,\n        )\n\n        rows, cols = params[\"shape\"][:2]\n        return normalize_bboxes(\n            result,\n            (rows + pad_top + pad_bottom, cols + pad_left + pad_right),\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_keypoints(\n            keypoints,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=params[\"shape\"][:2],\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if isinstance(self.padding, Real):\n            pad_top = pad_bottom = pad_left = pad_right = self.padding\n        elif isinstance(self.padding, (tuple, list)):\n            if len(self.padding) == NUM_PADS_XY:\n                pad_left = pad_right = self.padding[0]\n                pad_top = pad_bottom = self.padding[1]\n            elif len(self.padding) == NUM_PADS_ALL_SIDES:\n                pad_left, pad_top, pad_right, pad_bottom = self.padding  # type: ignore[misc]\n            else:\n                raise TypeError(\n                    \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n                )\n        else:\n            raise TypeError(\n                \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n            )\n\n        return {\n            \"pad_top\": pad_top,\n            \"pad_bottom\": pad_bottom,\n            \"pad_left\": pad_left,\n            \"pad_right\": pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"padding\",\n            \"fill\",\n            \"fill_mask\",\n            \"border_mode\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.PadIfNeeded","title":"class PadIfNeeded (min_height=1024, min_width=1024, pad_height_divisor=None, pad_width_divisor=None, position='center', border_mode=4, value=None, mask_value=None, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of an image if the image dimensions are less than the specified minimum dimensions. If the pad_height_divisor or pad_width_divisor is specified, the function additionally ensures that the image dimensions are divisible by these values.

Parameters:

Name Type Description min_height int | None

Minimum desired height of the image. Ensures image height is at least this value. If not specified, pad_height_divisor must be provided.

min_width int | None

Minimum desired width of the image. Ensures image width is at least this value. If not specified, pad_width_divisor must be provided.

pad_height_divisor int | None

If set, pads the image height to make it divisible by this value. If not specified, min_height must be provided.

pad_width_divisor int | None

If set, pads the image width to make it divisible by this value. If not specified, min_width must be provided.

position Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]

Position where the image is to be placed after padding. Default is 'center'.

border_mode int

Specifies the border mode to use if padding is required. The default is cv2.BORDER_REFLECT_101.

fill ColorType | None

Value to fill the border pixels if the border mode is cv2.BORDER_CONSTANT. Default is None.

fill_mask ColorType | None

Similar to fill but used for padding masks. Default is None.

p float

Probability of applying the transform. Default is 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • Either min_height or pad_height_divisor must be set, but not both.
  • Either min_width or pad_width_divisor must be set, but not both.
  • If border_mode is set to cv2.BORDER_CONSTANT, value must be provided.
  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).
  • For bounding boxes, the coordinates will be adjusted to account for the padding.
  • For keypoints, their positions will be shifted according to the padding.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> padded_image = transformed['image']\n>>> padded_mask = transformed['mask']\n>>> adjusted_bboxes = transformed['bboxes']\n>>> adjusted_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PadIfNeeded(Pad):\n    \"\"\"Pads the sides of an image if the image dimensions are less than the specified minimum dimensions.\n    If the `pad_height_divisor` or `pad_width_divisor` is specified, the function additionally ensures\n    that the image dimensions are divisible by these values.\n\n    Args:\n        min_height (int | None): Minimum desired height of the image. Ensures image height is at least this value.\n            If not specified, pad_height_divisor must be provided.\n        min_width (int | None): Minimum desired width of the image. Ensures image width is at least this value.\n            If not specified, pad_width_divisor must be provided.\n        pad_height_divisor (int | None): If set, pads the image height to make it divisible by this value.\n            If not specified, min_height must be provided.\n        pad_width_divisor (int | None): If set, pads the image width to make it divisible by this value.\n            If not specified, min_width must be provided.\n        position (Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]):\n            Position where the image is to be placed after padding. Default is 'center'.\n        border_mode (int): Specifies the border mode to use if padding is required.\n            The default is `cv2.BORDER_REFLECT_101`.\n        fill (ColorType | None): Value to fill the border pixels if the border mode is `cv2.BORDER_CONSTANT`.\n            Default is None.\n        fill_mask (ColorType | None): Similar to `fill` but used for padding masks. Default is None.\n        p (float): Probability of applying the transform. Default is 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Either `min_height` or `pad_height_divisor` must be set, but not both.\n        - Either `min_width` or `pad_width_divisor` must be set, but not both.\n        - If `border_mode` is set to `cv2.BORDER_CONSTANT`, `value` must be provided.\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).\n        - For bounding boxes, the coordinates will be adjusted to account for the padding.\n        - For keypoints, their positions will be shifted according to the padding.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> padded_image = transformed['image']\n        >>> padded_mask = transformed['mask']\n        >>> adjusted_bboxes = transformed['bboxes']\n        >>> adjusted_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        min_height: int | None = Field(ge=1)\n        min_width: int | None = Field(ge=1)\n        pad_height_divisor: int | None = Field(ge=1)\n        pad_width_divisor: int | None = Field(ge=1)\n        position: PositionType\n        border_mode: BorderModeType\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        @model_validator(mode=\"after\")\n        def validate_divisibility(self) -> Self:\n            if (self.min_height is None) == (self.pad_height_divisor is None):\n                msg = \"Only one of 'min_height' and 'pad_height_divisor' parameters must be set\"\n                raise ValueError(msg)\n            if (self.min_width is None) == (self.pad_width_divisor is None):\n                msg = \"Only one of 'min_width' and 'pad_width_divisor' parameters must be set\"\n                raise ValueError(msg)\n\n            if self.border_mode == cv2.BORDER_CONSTANT and self.fill is None:\n                msg = \"If 'border_mode' is set to 'BORDER_CONSTANT', 'fill' must be provided.\"\n                raise ValueError(msg)\n\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n\n            return self\n\n    def __init__(\n        self,\n        min_height: int | None = 1024,\n        min_width: int | None = 1024,\n        pad_height_divisor: int | None = None,\n        pad_width_divisor: int | None = None,\n        position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        # Initialize with dummy padding that will be calculated later\n        super().__init__(\n            padding=0,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            p=p,\n        )\n        self.min_height = min_height\n        self.min_width = min_width\n        self.pad_height_divisor = pad_height_divisor\n        self.pad_width_divisor = pad_width_divisor\n        self.position = position\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=params[\"shape\"][:2],\n            min_height=self.min_height,\n            min_width=self.min_width,\n            pad_height_divisor=self.pad_height_divisor,\n            pad_width_divisor=self.pad_width_divisor,\n        )\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_height\",\n            \"min_width\",\n            \"pad_height_divisor\",\n            \"pad_width_divisor\",\n            \"position\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Perspective","title":"class Perspective (scale=(0.05, 0.1), keep_size=True, pad_mode=None, pad_val=None, mask_pad_val=None, fit_output=False, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random four point perspective transformation to the input.

Parameters:

Name Type Description scale float or tuple of float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Default: (0.05, 0.1).

keep_size bool

Whether to resize image back to its original size after applying the perspective transform. If set to False, the resulting images may end up having different shapes. Default: True.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fit_output bool

If True, the image plane size and position will be adjusted to still capture the whole image after perspective transformation. This is followed by image resizing if keep_size is set to True. If False, parts of the transformed image may be outside of the image plane. This setting should not be set to True when using large scale values as it could lead to very large images. Default: False.

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

This transformation creates a perspective effect by randomly moving the four corners of the image. The amount of movement is controlled by the 'scale' parameter.

When 'keep_size' is True, the output image will have the same size as the input image, which may cause some parts of the transformed image to be cut off or padded.

When 'fit_output' is True, the transformation ensures that the entire transformed image is visible, which may result in a larger output image if keep_size is False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n... ])\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Perspective(DualTransform):\n    \"\"\"Apply random four point perspective transformation to the input.\n\n    Args:\n        scale (float or tuple of float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Default: (0.05, 0.1).\n        keep_size (bool): Whether to resize image back to its original size after applying the perspective transform.\n            If set to False, the resulting images may end up having different shapes.\n            Default: True.\n        border_mode (OpenCV flag): OpenCV border mode used for padding.\n            Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n        fill_mask (ColorType): Padding value for mask if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fit_output (bool): If True, the image plane size and position will be adjusted to still capture\n            the whole image after perspective transformation. This is followed by image resizing if keep_size is set\n            to True. If False, parts of the transformed image may be outside of the image plane.\n            This setting should not be set to True when using large scale values as it could lead to very large images.\n            Default: False.\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transformation creates a perspective effect by randomly moving the four corners of the image.\n        The amount of movement is controlled by the 'scale' parameter.\n\n        When 'keep_size' is True, the output image will have the same size as the input image,\n        which may cause some parts of the transformed image to be cut off or padded.\n\n        When 'fit_output' is True, the transformation ensures that the entire transformed image is visible,\n        which may result in a larger output image if keep_size is False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n        ... ])\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        keep_size: bool\n        pad_mode: BorderModeType | None\n        pad_val: ColorType | None\n        mask_pad_val: ColorType | None\n        fit_output: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def validate_deprecated_fields(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_val is not None:\n                warn(\"pad_val is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_val\n            if self.mask_pad_val is not None:\n                warn(\"mask_pad_val is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_pad_val\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.05, 0.1),\n        keep_size: bool = True,\n        pad_mode: int | None = None,\n        pad_val: ColorType | None = None,\n        mask_pad_val: ColorType | None = None,\n        fit_output: bool = False,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply=always_apply)\n        self.scale = cast(tuple[float, float], scale)\n        self.keep_size = keep_size\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.fit_output = fit_output\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            img,\n            matrix,\n            max_width,\n            max_height,\n            self.fill,\n            self.border_mode,\n            self.keep_size,\n            self.interpolation,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            mask,\n            matrix,\n            max_width,\n            max_height,\n            self.fill_mask,\n            self.border_mode,\n            self.keep_size,\n            self.mask_interpolation,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        matrix_bbox: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_bboxes(\n            bboxes,\n            params[\"shape\"],\n            matrix_bbox,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_keypoints(\n            keypoints,\n            params[\"shape\"],\n            matrix,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        scale = self.py_random.uniform(*self.scale)\n\n        points = fgeometric.generate_perspective_points(\n            image_shape,\n            scale,\n            self.random_generator,\n        )\n        points = fgeometric.order_points(points)\n\n        matrix, max_width, max_height = fgeometric.compute_perspective_params(\n            points,\n            image_shape,\n        )\n\n        if self.fit_output:\n            matrix, max_width, max_height = fgeometric.expand_transform(\n                matrix,\n                image_shape,\n            )\n\n        return {\n            \"matrix\": matrix,\n            \"max_height\": max_height,\n            \"max_width\": max_width,\n            \"matrix_bbox\": matrix,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"keep_size\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"fit_output\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.PiecewiseAffine","title":"class PiecewiseAffine (scale=(0.03, 0.05), nb_rows=(4, 4), nb_cols=(4, 4), interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, absolute_scale=False, p=0.5, always_apply=None, keypoints_threshold=0.01) [view source on GitHub]","text":"

Apply piecewise affine transformations to the input image.

This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points around via affine transformations. This leads to local distortions in the image.

Parameters:

Name Type Description scale tuple[float, float] | float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Recommended values are in the range (0.01, 0.05) for small distortions, and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).

nb_rows tuple[int, int] | int

Number of rows of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of rows. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

nb_cols tuple[int, int] | int

Number of columns of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of columns. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

absolute_scale bool

If set to True, the value of the scale parameter will be treated as an absolute pixel value. If set to False, it will be treated as a fraction of the image height and width. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This augmentation is very slow. Consider using ElasticTransform instead, which is at least 10x faster.
  • The augmentation may not always produce visible effects, especially with small scale values.
  • For keypoints and bounding boxes, the transformation might move them outside the image boundaries. In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PiecewiseAffine(BaseDistortion):\n    \"\"\"Apply piecewise affine transformations to the input image.\n\n    This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points\n    around via affine transformations. This leads to local distortions in the image.\n\n    Args:\n        scale (tuple[float, float] | float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Recommended values are in the range (0.01, 0.05) for small distortions,\n            and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).\n        nb_rows (tuple[int, int] | int): Number of rows of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of rows.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        nb_cols (tuple[int, int] | int): Number of columns of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of columns.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        absolute_scale (bool): If set to True, the value of the scale parameter will be treated as an absolute\n            pixel value. If set to False, it will be treated as a fraction of the image height and width.\n            Default: False.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This augmentation is very slow. Consider using `ElasticTransform` instead, which is at least 10x faster.\n        - The augmentation may not always produce visible effects, especially with small scale values.\n        - For keypoints and bounding boxes, the transformation might move them outside the image boundaries.\n          In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed[\"image\"]\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        nb_rows: ScaleIntType\n        nb_cols: ScaleIntType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        cval: int | None = Field(deprecated=\"Deprecated. Does not have any effect.\")\n        cval_mask: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = Field(\n            deprecated=\"Deprecated. Does not have any effects.\",\n        )\n\n        absolute_scale: bool\n        keypoints_threshold: float = Field(\n            deprecated=\"This parameter is not used anymore\",\n        )\n\n        @field_validator(\"nb_rows\", \"nb_cols\")\n        @classmethod\n        def process_range(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 2, BIG_INTEGER\n            result = to_tuple(value, value)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.03, 0.05),\n        nb_rows: ScaleIntType = (4, 4),\n        nb_cols: ScaleIntType = (4, 4),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: int | None = None,\n        cval_mask: int | None = None,\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = None,\n        absolute_scale: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n        keypoints_threshold: float = 0.01,\n    ):\n        super().__init__(\n            p=p,\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n        )\n\n        warn(\n            \"This augmenter is very slow. Try to use ``ElasticTransform`` instead, which is at least 10x faster.\",\n            stacklevel=2,\n        )\n\n        self.scale = cast(tuple[float, float], scale)\n        self.nb_rows = cast(tuple[int, int], nb_rows)\n        self.nb_cols = cast(tuple[int, int], nb_cols)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.absolute_scale = absolute_scale\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"nb_rows\",\n            \"nb_cols\",\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"absolute_scale\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        nb_rows = np.clip(self.py_random.randint(*self.nb_rows), 2, None)\n        nb_cols = np.clip(self.py_random.randint(*self.nb_cols), 2, None)\n        scale = self.py_random.uniform(*self.scale)\n\n        map_x, map_y = fgeometric.create_piecewise_affine_maps(\n            image_shape=image_shape,\n            grid=(nb_rows, nb_cols),\n            scale=scale,\n            absolute_scale=self.absolute_scale,\n            random_generator=self.random_generator,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.RandomGridShuffle","title":"class RandomGridShuffle (grid=(3, 3), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shuffles the grid's cells on an image, mask, or keypoints, effectively rearranging patches within the image. This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.

Parameters:

Name Type Description grid tuple[int, int]

Size of the grid for splitting the image into cells. Each cell is shuffled randomly. For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled. Default: (3, 3)

p float

Probability that the transform will be applied. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This transform maintains consistency across all targets. If applied to an image and its corresponding mask or keypoints, the same shuffling will be applied to all.
  • The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2)) for the transform to have any effect.
  • Keypoints are moved along with their corresponding grid cell.
  • This transform could be useful when only micro features are important for the model, and memorizing the global structure could be harmful. For example:
  • Identifying the type of cell phone used to take a picture based on micro artifacts generated by phone post-processing algorithms, rather than the semantic features of the photo. See more at https://ieeexplore.ieee.org/abstract/document/8622031
  • Identifying stress, glucose, hydration levels based on skin images.

Mathematical Formulation: 1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter. 2. A random permutation P of integers from 0 to (mn - 1) is generated. 3. Each cell in the grid is assigned a number from 0 to (mn - 1) in row-major order. 4. The cells are then rearranged according to the permutation P.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4]\n... ])\n>>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image might look like this (one possible outcome):\n# [[4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1]]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class RandomGridShuffle(DualTransform):\n    \"\"\"Randomly shuffles the grid's cells on an image, mask, or keypoints,\n    effectively rearranging patches within the image.\n    This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.\n\n    Args:\n        grid (tuple[int, int]): Size of the grid for splitting the image into cells. Each cell is shuffled randomly.\n            For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled.\n            Default: (3, 3)\n        p (float): Probability that the transform will be applied. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform maintains consistency across all targets. If applied to an image and its corresponding\n          mask or keypoints, the same shuffling will be applied to all.\n        - The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2))\n          for the transform to have any effect.\n        - Keypoints are moved along with their corresponding grid cell.\n        - This transform could be useful when only micro features are important for the model, and memorizing\n          the global structure could be harmful. For example:\n          - Identifying the type of cell phone used to take a picture based on micro artifacts generated by\n            phone post-processing algorithms, rather than the semantic features of the photo.\n            See more at https://ieeexplore.ieee.org/abstract/document/8622031\n          - Identifying stress, glucose, hydration levels based on skin images.\n\n    Mathematical Formulation:\n        1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter.\n        2. A random permutation P of integers from 0 to (m*n - 1) is generated.\n        3. Each cell in the grid is assigned a number from 0 to (m*n - 1) in row-major order.\n        4. The cells are then rearranged according to the permutation P.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4]\n        ... ])\n        >>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image might look like this (one possible outcome):\n        # [[4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1]]\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        grid: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        grid: tuple[int, int] = (3, 3),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.grid = grid\n\n    def apply(\n        self,\n        img: np.ndarray,\n        tiles: np.ndarray,\n        mapping: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_image(img, tiles, mapping)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n        bboxes_returned = fgeometric.bboxes_grid_shuffle(\n            bboxes_denorm,\n            tiles,\n            mapping,\n            image_shape,\n            min_area=processor.params.min_area,\n            min_visibility=processor.params.min_visibility,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_keypoints(keypoints, tiles, mapping)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        original_tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.grid,\n            self.random_generator,\n        )\n        shape_groups = fgeometric.create_shape_groups(original_tiles)\n        mapping = fgeometric.shuffle_tiles_within_shape_groups(\n            shape_groups,\n            self.random_generator,\n        )\n\n        return {\"tiles\": original_tiles, \"mapping\": mapping}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"grid\",)\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ShiftScaleRotate","title":"class ShiftScaleRotate (shift_limit=(-0.0625, 0.0625), scale_limit=(-0.1, 0.1), rotate_limit=(-45, 45), interpolation=1, border_mode=4, value=None, mask_value=None, shift_limit_x=None, shift_limit_y=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly apply affine transforms: translate, scale and rotate the input.

Parameters:

Name Type Description shift_limit float, float) or float

shift factor range for both height and width. If shift_limit is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).

scale_limit float, float) or float

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

rotate_limit int, int) or int

rotation range. If rotate_limit is a single int value, the range will be (-rotate_limit, rotate_limit). Default: (-45, 45).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

shift_limit_x float, float) or float

shift factor range for width. If it is set then this value instead of shift_limit will be used for shifting width. If shift_limit_x is a single float value, the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in the range [-1, 1]. Default: None.

shift_limit_y float, float) or float

shift factor range for height. If it is set then this value instead of shift_limit will be used for shifting height. If shift_limit_y is a single float value, the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie in the range [-, 1]. Default: None.

rotate_method str

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\". Default: \"largest_box\"

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ShiftScaleRotate(Affine):\n    \"\"\"Randomly apply affine transforms: translate, scale and rotate the input.\n\n    Args:\n        shift_limit ((float, float) or float): shift factor range for both height and width. If shift_limit\n            is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and\n            upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).\n        scale_limit ((float, float) or float): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        rotate_limit ((int, int) or int): rotation range. If rotate_limit is a single int value, the\n            range will be (-rotate_limit, rotate_limit). Default: (-45, 45).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        shift_limit_x ((float, float) or float): shift factor range for width. If it is set then this value\n            instead of shift_limit will be used for shifting width.  If shift_limit_x is a single float value,\n            the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in\n            the range [-1, 1]. Default: None.\n        shift_limit_y ((float, float) or float): shift factor range for height. If it is set then this value\n            instead of shift_limit will be used for shifting height.  If shift_limit_y is a single float value,\n            the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie\n            in the range [-, 1]. Default: None.\n        rotate_method (str): rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\".\n            Default: \"largest_box\"\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        shift_limit: SymmetricRangeType\n        scale_limit: SymmetricRangeType\n        rotate_limit: SymmetricRangeType\n        interpolation: InterpolationType\n        border_mode: BorderModeType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType = 0\n        fill_mask: ColorType = 0\n\n        shift_limit_x: ScaleFloatType | None\n        shift_limit_y: ScaleFloatType | None\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def check_shift_limit(self) -> Self:\n            bounds = -1, 1\n            self.shift_limit_x = to_tuple(\n                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_x, *bounds, \"shift_limit_x\")\n            self.shift_limit_y = to_tuple(\n                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_y, *bounds, \"shift_limit_y\")\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> ScaleFloatType:\n            bounds = 0, float(\"inf\")\n            result = to_tuple(value, bias=1.0)\n            check_range(result, *bounds, str(info.field_name))\n            return result\n\n    def __init__(\n        self,\n        shift_limit: ScaleFloatType = (-0.0625, 0.0625),\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        rotate_limit: ScaleFloatType = (-45, 45),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        shift_limit_x: ScaleFloatType | None = None,\n        shift_limit_y: ScaleFloatType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: InterpolationType = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        shift_limit_x = cast(tuple[float, float], shift_limit_x)\n        shift_limit_y = cast(tuple[float, float], shift_limit_y)\n        super().__init__(\n            scale=scale_limit,\n            translate_percent={\"x\": shift_limit_x, \"y\": shift_limit_y},\n            rotate=rotate_limit,\n            shear=(0, 0),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            fit_output=False,\n            keep_ratio=False,\n            rotate_method=rotate_method,\n            always_apply=always_apply,\n            p=p,\n        )\n        warn(\n            \"ShiftScaleRotate is deprecated. Please use Affine transform instead.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        self.shift_limit_x = shift_limit_x\n        self.shift_limit_y = shift_limit_y\n\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"shift_limit_x\": self.shift_limit_x,\n            \"shift_limit_y\": self.shift_limit_y,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n            \"rotate_limit\": self.rotate_limit,\n            \"interpolation\": self.interpolation,\n            \"border_mode\": self.border_mode,\n            \"fill\": self.fill,\n            \"fill_mask\": self.fill_mask,\n            \"rotate_method\": self.rotate_method,\n            \"mask_interpolation\": self.mask_interpolation,\n        }\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.ThinPlateSpline","title":"class ThinPlateSpline (scale_range=(0.2, 0.4), num_control_points=4, interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.

Imagine the image printed on a thin metal plate that can be bent and warped smoothly: - Control points act like pins pushing or pulling the plate - The plate resists sharp bending, creating smooth deformations - The transformation maintains continuity (no tears or folds) - Areas between control points are interpolated naturally

The transform works by: 1. Creating a regular grid of control points (like pins in the plate) 2. Randomly displacing these points (like pushing/pulling the pins) 3. Computing a smooth interpolation (like the plate bending) 4. Applying the resulting deformation to the image

Parameters:

Name Type Description scale_range tuple[float, float]

Range for random displacement of control points. Values should be in [0.0, 1.0]: - 0.0: No displacement (identity transform) - 0.1: Subtle warping - 0.2-0.4: Moderate deformation (recommended range) - 0.5+: Strong warping Default: (0.2, 0.4)

num_control_points int

Number of control points per side. Creates a grid of num_control_points x num_control_points points. - 2: Minimal deformation (affine-like) - 3-4: Moderate flexibility (recommended) - 5+: More local deformation control Must be >= 2. Default: 4

interpolation int

OpenCV interpolation flag. Used for image sampling. See also: cv2.INTER_* Default: cv2.INTER_LINEAR

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • The transformation preserves smoothness and continuity
  • Stronger scale values may create more extreme deformations
  • Higher number of control points allows more local deformations
  • The same deformation is applied consistently to all targets

Examples:

Python
>>> import albumentations as A\n>>> # Basic usage\n>>> transform = A.ThinPlateSpline()\n>>>\n>>> # Subtle deformation\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.1, 0.2),\n...     num_control_points=3\n... )\n>>>\n>>> # Strong warping with fine control\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.3, 0.5),\n...     num_control_points=5,\n... )\n

References

  • \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\" by F.L. Bookstein https://doi.org/10.1109/34.24792

  • Thin Plate Splines in Computer Vision: https://en.wikipedia.org/wiki/Thin_plate_spline

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline

See Also: - ElasticTransform: For different type of non-rigid deformation - GridDistortion: For grid-based warping - OpticalDistortion: For lens-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ThinPlateSpline(BaseDistortion):\n    r\"\"\"Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.\n\n    Imagine the image printed on a thin metal plate that can be bent and warped smoothly:\n    - Control points act like pins pushing or pulling the plate\n    - The plate resists sharp bending, creating smooth deformations\n    - The transformation maintains continuity (no tears or folds)\n    - Areas between control points are interpolated naturally\n\n    The transform works by:\n    1. Creating a regular grid of control points (like pins in the plate)\n    2. Randomly displacing these points (like pushing/pulling the pins)\n    3. Computing a smooth interpolation (like the plate bending)\n    4. Applying the resulting deformation to the image\n\n\n    Args:\n        scale_range (tuple[float, float]): Range for random displacement of control points.\n            Values should be in [0.0, 1.0]:\n            - 0.0: No displacement (identity transform)\n            - 0.1: Subtle warping\n            - 0.2-0.4: Moderate deformation (recommended range)\n            - 0.5+: Strong warping\n            Default: (0.2, 0.4)\n\n        num_control_points (int): Number of control points per side.\n            Creates a grid of num_control_points x num_control_points points.\n            - 2: Minimal deformation (affine-like)\n            - 3-4: Moderate flexibility (recommended)\n            - 5+: More local deformation control\n            Must be >= 2. Default: 4\n\n        interpolation (int): OpenCV interpolation flag. Used for image sampling.\n            See also: cv2.INTER_*\n            Default: cv2.INTER_LINEAR\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transformation preserves smoothness and continuity\n        - Stronger scale values may create more extreme deformations\n        - Higher number of control points allows more local deformations\n        - The same deformation is applied consistently to all targets\n\n    Example:\n        >>> import albumentations as A\n        >>> # Basic usage\n        >>> transform = A.ThinPlateSpline()\n        >>>\n        >>> # Subtle deformation\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.1, 0.2),\n        ...     num_control_points=3\n        ... )\n        >>>\n        >>> # Strong warping with fine control\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.3, 0.5),\n        ...     num_control_points=5,\n        ... )\n\n    References:\n        - \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\"\n          by F.L. Bookstein\n          https://doi.org/10.1109/34.24792\n\n        - Thin Plate Splines in Computer Vision:\n          https://en.wikipedia.org/wiki/Thin_plate_spline\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline\n\n    See Also:\n        - ElasticTransform: For different type of non-rigid deformation\n        - GridDistortion: For grid-based warping\n        - OpticalDistortion: For lens-like distortions\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        scale_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        num_control_points: int = Field(ge=2)\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.2, 0.4),\n        num_control_points: int = 4,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale_range = scale_range\n        self.num_control_points = num_control_points\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        # Create regular grid of control points\n        grid_size = self.num_control_points\n        x = np.linspace(0, 1, grid_size)\n        y = np.linspace(0, 1, grid_size)\n        src_points = np.stack(np.meshgrid(x, y), axis=-1).reshape(-1, 2)\n\n        # Add random displacement to destination points\n        scale = self.py_random.uniform(*self.scale_range) / 10\n        dst_points = src_points + self.random_generator.normal(\n            0,\n            scale,\n            src_points.shape,\n        )\n\n        # Compute TPS weights\n        weights, affine = fgeometric.compute_tps_weights(src_points, dst_points)\n\n        # Create grid of points\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        points = np.stack([x.flatten(), y.flatten()], axis=1).astype(np.float32)\n\n        # Transform points\n        transformed = fgeometric.tps_transform(\n            points / [width, height],\n            src_points,\n            weights,\n            affine,\n        )\n        transformed *= [width, height]\n\n        return {\n            \"map_x\": transformed[:, 0].reshape(height, width).astype(np.float32),\n            \"map_y\": transformed[:, 1].reshape(height, width).astype(np.float32),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale_range\",\n            \"num_control_points\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.Transpose","title":"class Transpose [view source on GitHub]","text":"

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.Transpose(p=1.0)\n>>> result = transform(image=image)\n>>> transposed_image = result['image']\n>>> print(transposed_image)\n[[[ 1  2  3]\n  [ 7  8  9]]\n [[ 4  5  6]\n  [10 11 12]]]\n# The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Transpose(DualTransform):\n    \"\"\"Transpose the input by swapping its rows and columns.\n\n    This transform flips the image over its main diagonal, effectively switching its width and height.\n    It's equivalent to a 90-degree rotation followed by a horizontal flip.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The dimensions of the output will be swapped compared to the input. For example,\n          an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).\n        - This transform is its own inverse. Applying it twice will return the original input.\n        - For multi-channel images (like RGB), the channels are preserved in their original order.\n        - Bounding boxes will have their coordinates adjusted to match the new image dimensions.\n        - Keypoints will have their x and y coordinates swapped.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (y_min, x_min, y_max, x_max)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (y, x)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.Transpose(p=1.0)\n        >>> result = transform(image=image)\n        >>> transposed_image = result['image']\n        >>> print(transposed_image)\n        [[[ 1  2  3]\n          [ 7  8  9]]\n         [[ 4  5  6]\n          [10 11 12]]]\n        # The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.transpose(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_transpose(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_transpose(keypoints)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/#albumentations.augmentations.geometric.transforms.VerticalFlip","title":"class VerticalFlip [view source on GitHub]","text":"

Flip the input vertically around the x-axis.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.VerticalFlip(p=1.0)\n>>> result = transform(image=image)\n>>> flipped_image = result['image']\n>>> print(flipped_image)\n[[[ 7  8  9]\n  [10 11 12]]\n [[ 1  2  3]\n  [ 4  5  6]]]\n# The original image is flipped vertically, with rows reversed\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class VerticalFlip(DualTransform):\n    \"\"\"Flip the input vertically around the x-axis.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform flips the image upside down. The top of the image becomes the bottom and vice versa.\n        - The dimensions of the image remain unchanged.\n        - For multi-channel images (like RGB), each channel is flipped independently.\n        - Bounding boxes are adjusted to match their new positions in the flipped image.\n        - Keypoints are moved to their new positions in the flipped image.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (x_min, H-y_max, x_max, H-y_min)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (x, H-y)\n        where H is the height of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.VerticalFlip(p=1.0)\n        >>> result = transform(image=image)\n        >>> flipped_image = result['image']\n        >>> print(flipped_image)\n        [[[ 7  8  9]\n          [10 11 12]]\n         [[ 1  2  3]\n          [ 4  5  6]]]\n        # The original image is flipped vertically, with rows reversed\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return vflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_vflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_vflip(keypoints, params[\"shape\"][0])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/","title":"Transforms (augmentations.transforms)","text":""},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.AdditiveNoise","title":"class AdditiveNoise (noise_type='uniform', spatial_mode='constant', noise_params=None, approximation=1.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random noise to image channels using various noise distributions.

This transform generates noise using different probability distributions and applies it to image channels. The noise can be generated in three spatial modes and supports multiple noise distributions, each with configurable parameters.

Parameters:

Name Type Description noise_type Literal['uniform', 'gaussian', 'laplace', 'beta']

Type of noise distribution to use. Options: - \"uniform\": Uniform distribution, good for simple random perturbations - \"gaussian\": Normal distribution, models natural random processes - \"laplace\": Similar to Gaussian but with heavier tails, good for outliers - \"beta\": Flexible bounded distribution, can be symmetric or skewed

spatial_mode Literal['constant', 'per_pixel', 'shared']

How to generate and apply the noise. Options: - \"constant\": One noise value per channel, fastest - \"per_pixel\": Independent noise value for each pixel and channel, slowest - \"shared\": One noise map shared across all channels, medium speed

approximation float

float in [0, 1], default=1.0 Controls noise generation speed vs quality tradeoff. - 1.0: Generate full resolution noise (slowest, highest quality) - 0.5: Generate noise at half resolution and upsample - 0.25: Generate noise at quarter resolution and upsample Only affects 'per_pixel' and 'shared' spatial modes.

noise_params dict[str, Any] | None

Parameters for the chosen noise distribution. Must match the noise_type:

uniform: ranges: list[tuple[float, float]] List of (min, max) ranges for each channel. Each range must be in [-1, 1]. If only one range is provided, it will be used for all channels.

    [(-0.2, 0.2)]  # Same range for all channels\n    [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]  # Different ranges for RGB\n

gaussian: mean_range: tuple[float, float], default (0.0, 0.0) Range for sampling mean value, in [-1, 1] std_range: tuple[float, float], default (0.1, 0.1) Range for sampling standard deviation, in [0, 1]

laplace: mean_range: tuple[float, float], default (0.0, 0.0) Range for sampling location parameter, in [-1, 1] scale_range: tuple[float, float], default (0.1, 0.1) Range for sampling scale parameter, in [0, 1]

beta: alpha_range: tuple[float, float], default (0.5, 1.5) Value < 1 = U-shaped, Value > 1 = Bell-shaped Range for sampling first shape parameter, in (0, inf) beta_range: tuple[float, float], default (0.5, 1.5) Value < 1 = U-shaped, Value > 1 = Bell-shaped Range for sampling second shape parameter, in (0, inf) scale_range: tuple[float, float], default (0.1, 0.3) Smaller scale for subtler noise Range for sampling output scale, in [0, 1]

Note

Performance considerations: - \"constant\" mode is fastest as it generates only C values (C = number of channels) - \"shared\" mode generates HxW values and reuses them for all channels - \"per_pixel\" mode generates HxWxC values, slowest but most flexible

Distribution characteristics: - uniform: Equal probability within range, good for simple perturbations - gaussian: Bell-shaped, symmetric, good for natural noise - laplace: Like gaussian but with heavier tails, good for outliers - beta: Very flexible shape, can be uniform, bell-shaped, or U-shaped

Implementation details: - All noise is generated in normalized range and scaled by image max value - For uint8 images, final noise range is [-255, 255] - For float images, final noise range is [-1, 1]

Examples:

Constant RGB shift with different ranges per channel:

Python
>>> transform = AdditiveNoise(\n...     noise_type=\"uniform\",\n...     spatial_mode=\"constant\",\n...     noise_params={\"ranges\": [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]}\n... )\n

Gaussian noise shared across channels:

Python
>>> transform = AdditiveNoise(\n...     noise_type=\"gaussian\",\n...     spatial_mode=\"shared\",\n...     noise_params={\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)}\n... )\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class AdditiveNoise(ImageOnlyTransform):\n    \"\"\"Apply random noise to image channels using various noise distributions.\n\n    This transform generates noise using different probability distributions and applies it\n    to image channels. The noise can be generated in three spatial modes and supports\n    multiple noise distributions, each with configurable parameters.\n\n    Args:\n        noise_type: Type of noise distribution to use. Options:\n            - \"uniform\": Uniform distribution, good for simple random perturbations\n            - \"gaussian\": Normal distribution, models natural random processes\n            - \"laplace\": Similar to Gaussian but with heavier tails, good for outliers\n            - \"beta\": Flexible bounded distribution, can be symmetric or skewed\n\n        spatial_mode: How to generate and apply the noise. Options:\n            - \"constant\": One noise value per channel, fastest\n            - \"per_pixel\": Independent noise value for each pixel and channel, slowest\n            - \"shared\": One noise map shared across all channels, medium speed\n\n        approximation: float in [0, 1], default=1.0\n            Controls noise generation speed vs quality tradeoff.\n            - 1.0: Generate full resolution noise (slowest, highest quality)\n            - 0.5: Generate noise at half resolution and upsample\n            - 0.25: Generate noise at quarter resolution and upsample\n            Only affects 'per_pixel' and 'shared' spatial modes.\n\n        noise_params: Parameters for the chosen noise distribution.\n            Must match the noise_type:\n\n            uniform:\n                ranges: list[tuple[float, float]]\n                    List of (min, max) ranges for each channel.\n                    Each range must be in [-1, 1].\n                    If only one range is provided, it will be used for all channels.\n\n                    [(-0.2, 0.2)]  # Same range for all channels\n                    [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]  # Different ranges for RGB\n\n            gaussian:\n                mean_range: tuple[float, float], default (0.0, 0.0)\n                    Range for sampling mean value, in [-1, 1]\n                std_range: tuple[float, float], default (0.1, 0.1)\n                    Range for sampling standard deviation, in [0, 1]\n\n            laplace:\n                mean_range: tuple[float, float], default (0.0, 0.0)\n                    Range for sampling location parameter, in [-1, 1]\n                scale_range: tuple[float, float], default (0.1, 0.1)\n                    Range for sampling scale parameter, in [0, 1]\n\n            beta:\n                alpha_range: tuple[float, float], default (0.5, 1.5)\n                    Value < 1 = U-shaped, Value > 1 = Bell-shaped\n                    Range for sampling first shape parameter, in (0, inf)\n                beta_range: tuple[float, float], default (0.5, 1.5)\n                    Value < 1 = U-shaped, Value > 1 = Bell-shaped\n                    Range for sampling second shape parameter, in (0, inf)\n                scale_range: tuple[float, float], default (0.1, 0.3)\n                    Smaller scale for subtler noise\n                    Range for sampling output scale, in [0, 1]\n\n    Note:\n        Performance considerations:\n            - \"constant\" mode is fastest as it generates only C values (C = number of channels)\n            - \"shared\" mode generates HxW values and reuses them for all channels\n            - \"per_pixel\" mode generates HxWxC values, slowest but most flexible\n\n        Distribution characteristics:\n            - uniform: Equal probability within range, good for simple perturbations\n            - gaussian: Bell-shaped, symmetric, good for natural noise\n            - laplace: Like gaussian but with heavier tails, good for outliers\n            - beta: Very flexible shape, can be uniform, bell-shaped, or U-shaped\n\n        Implementation details:\n            - All noise is generated in normalized range and scaled by image max value\n            - For uint8 images, final noise range is [-255, 255]\n            - For float images, final noise range is [-1, 1]\n\n    Examples:\n        Constant RGB shift with different ranges per channel:\n        >>> transform = AdditiveNoise(\n        ...     noise_type=\"uniform\",\n        ...     spatial_mode=\"constant\",\n        ...     noise_params={\"ranges\": [(-0.2, 0.2), (-0.1, 0.1), (-0.1, 0.1)]}\n        ... )\n\n        Gaussian noise shared across channels:\n        >>> transform = AdditiveNoise(\n        ...     noise_type=\"gaussian\",\n        ...     spatial_mode=\"shared\",\n        ...     noise_params={\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)}\n        ... )\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"]\n        spatial_mode: Literal[\"constant\", \"per_pixel\", \"shared\"]\n        noise_params: dict[str, Any] | None\n        approximation: float = Field(ge=0.0, le=1.0)\n\n        @model_validator(mode=\"after\")\n        def validate_noise_params(self) -> Self:\n            # Default parameters for each noise type\n            default_params = {\n                \"uniform\": {\n                    \"ranges\": [(-0.1, 0.1)],  # Single channel by default\n                },\n                \"gaussian\": {\"mean_range\": (0.0, 0.0), \"std_range\": (0.05, 0.15)},\n                \"laplace\": {\"mean_range\": (0.0, 0.0), \"scale_range\": (0.05, 0.15)},\n                \"beta\": {\n                    \"alpha_range\": (0.5, 1.5),\n                    \"beta_range\": (0.5, 1.5),\n                    \"scale_range\": (0.1, 0.3),\n                },\n            }\n\n            # Use default params if none provided\n            params_dict = self.noise_params if self.noise_params is not None else default_params[self.noise_type]\n\n            # Convert dict to appropriate NoiseParams object\n            params_class = {\n                \"uniform\": UniformParams,\n                \"gaussian\": GaussianParams,\n                \"laplace\": LaplaceParams,\n                \"beta\": BetaParams,\n            }[self.noise_type]\n\n            # Add noise_type to params if not present\n            params_dict = {**params_dict, \"noise_type\": self.noise_type}  # type: ignore[dict-item]\n            self.noise_params = params_class(**params_dict)\n\n            return self\n\n    def __init__(\n        self,\n        noise_type: Literal[\"uniform\", \"gaussian\", \"laplace\", \"beta\"] = \"uniform\",\n        spatial_mode: Literal[\"constant\", \"per_pixel\", \"shared\"] = \"constant\",\n        noise_params: dict[str, Any] | None = None,\n        approximation: float = 1.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.noise_type = noise_type\n        self.spatial_mode = spatial_mode\n        self.noise_params = noise_params\n        self.approximation = approximation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        noise_map: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_noise(img, noise_map)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n\n        noise_map = fmain.generate_noise(\n            noise_type=self.noise_type,\n            spatial_mode=self.spatial_mode,\n            shape=image.shape,\n            params=self.noise_params,\n            max_value=max_value,\n            approximation=self.approximation,\n            random_generator=self.random_generator,\n        )\n        return {\"noise_map\": noise_map}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"noise_type\", \"spatial_mode\", \"noise_params\", \"approximation\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.AutoContrast","title":"class AutoContrast (p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random auto contrast to images.

Auto contrast enhances image contrast by stretching the intensity range to use the full range while preserving relative intensities. For each color channel: 1. Compute histogram 2. Find cumulative percentiles 3. Clip and scale intensities to full range

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class AutoContrast(ImageOnlyTransform):\n    \"\"\"Apply random auto contrast to images.\n\n    Auto contrast enhances image contrast by stretching the intensity range\n    to use the full range while preserving relative intensities. For each\n    color channel:\n    1. Compute histogram\n    2. Find cumulative percentiles\n    3. Clip and scale intensities to full range\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.auto_contrast(img)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.BetaParams","title":"class BetaParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class BetaParams(NoiseParamsBase):\n    noise_type: Literal[\"beta\"] = \"beta\"\n    alpha_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0)),\n    ]\n    beta_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0)),\n    ]\n    scale_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.CLAHE","title":"class CLAHE (clip_limit=4.0, tile_grid_size=(8, 8), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.

CLAHE is an advanced method of improving the contrast in an image. Unlike regular histogram equalization, which operates on the entire image, CLAHE operates on small regions (tiles) in the image. This results in a more balanced equalization, preventing over-amplification of contrast in areas with initially low contrast.

Parameters:

Name Type Description clip_limit tuple[float, float] | float

Controls the contrast enhancement limit. - If a single float is provided, the range will be (1, clip_limit). - If a tuple of two floats is provided, it defines the range for random selection. Higher values allow for more contrast enhancement, but may also increase noise. Default: (1, 4)

tile_grid_size tuple[int, int]

Defines the number of tiles in the row and column directions. Format is (rows, columns). Smaller tile sizes can lead to more localized enhancements, while larger sizes give results closer to global histogram equalization. Default: (8, 8)

p float

Probability of applying the transform. Default: 0.5

Notes

  • Supports only RGB or grayscale images.
  • For color images, CLAHE is applied to the L channel in the LAB color space.
  • The clip limit determines the maximum slope of the cumulative histogram. A lower clip limit will result in more contrast limiting.
  • Tile grid size affects the adaptiveness of the method. More tiles increase local adaptiveness but can lead to an unnatural look if set too high.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1, 3

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.CLAHE(clip_limit=(1, 4), tile_grid_size=(8, 8), p=1.0)\n>>> result = transform(image=image)\n>>> clahe_image = result[\"image\"]\n

References

  • https://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html
  • Zuiderveld, Karel. \"Contrast Limited Adaptive Histogram Equalization.\" Graphic Gems IV. Academic Press Professional, Inc., 1994.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class CLAHE(ImageOnlyTransform):\n    \"\"\"Apply Contrast Limited Adaptive Histogram Equalization (CLAHE) to the input image.\n\n    CLAHE is an advanced method of improving the contrast in an image. Unlike regular histogram\n    equalization, which operates on the entire image, CLAHE operates on small regions (tiles)\n    in the image. This results in a more balanced equalization, preventing over-amplification\n    of contrast in areas with initially low contrast.\n\n    Args:\n        clip_limit (tuple[float, float] | float): Controls the contrast enhancement limit.\n            - If a single float is provided, the range will be (1, clip_limit).\n            - If a tuple of two floats is provided, it defines the range for random selection.\n            Higher values allow for more contrast enhancement, but may also increase noise.\n            Default: (1, 4)\n\n        tile_grid_size (tuple[int, int]): Defines the number of tiles in the row and column directions.\n            Format is (rows, columns). Smaller tile sizes can lead to more localized enhancements,\n            while larger sizes give results closer to global histogram equalization.\n            Default: (8, 8)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - Supports only RGB or grayscale images.\n        - For color images, CLAHE is applied to the L channel in the LAB color space.\n        - The clip limit determines the maximum slope of the cumulative histogram. A lower\n          clip limit will result in more contrast limiting.\n        - Tile grid size affects the adaptiveness of the method. More tiles increase local\n          adaptiveness but can lead to an unnatural look if set too high.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1, 3\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.CLAHE(clip_limit=(1, 4), tile_grid_size=(8, 8), p=1.0)\n        >>> result = transform(image=image)\n        >>> clahe_image = result[\"image\"]\n\n    References:\n        - https://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html\n        - Zuiderveld, Karel. \"Contrast Limited Adaptive Histogram Equalization.\"\n          Graphic Gems IV. Academic Press Professional, Inc., 1994.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        clip_limit: OnePlusFloatRangeType\n        tile_grid_size: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    def __init__(\n        self,\n        clip_limit: ScaleFloatType = 4.0,\n        tile_grid_size: tuple[int, int] = (8, 8),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.clip_limit = cast(tuple[float, float], clip_limit)\n        self.tile_grid_size = tile_grid_size\n\n    def apply(self, img: np.ndarray, clip_limit: float, **params: Any) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"CLAHE transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n\n        return fmain.clahe(img, clip_limit, self.tile_grid_size)\n\n    def get_params(self) -> dict[str, float]:\n        return {\"clip_limit\": self.py_random.uniform(*self.clip_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"clip_limit\", \"tile_grid_size\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ChannelShuffle","title":"class ChannelShuffle [view source on GitHub]","text":"

Randomly rearrange channels of the image.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ChannelShuffle(ImageOnlyTransform):\n    \"\"\"Randomly rearrange channels of the image.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    def apply(\n        self,\n        img: np.ndarray,\n        channels_shuffled: tuple[int, ...],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.channel_shuffle(img, channels_shuffled)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        ch_arr = list(range(params[\"shape\"][2]))\n        self.random_generator.shuffle(ch_arr)\n        return {\"channels_shuffled\": ch_arr}\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ChromaticAberration","title":"class ChromaticAberration (primary_distortion_limit=(-0.02, 0.02), secondary_distortion_limit=(-0.05, 0.05), mode='green_purple', interpolation=1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Add lateral chromatic aberration by distorting the red and blue channels of the input image.

Chromatic aberration is an optical effect that occurs when a lens fails to focus all colors to the same point. This transform simulates this effect by applying different radial distortions to the red and blue channels of the image, while leaving the green channel unchanged.

Parameters:

Name Type Description primary_distortion_limit tuple[float, float] | float

Range of the primary radial distortion coefficient. If a single float value is provided, the range will be (-primary_distortion_limit, primary_distortion_limit). This parameter controls the distortion in the center of the image: - Positive values result in pincushion distortion (edges bend inward) - Negative values result in barrel distortion (edges bend outward) Default: (-0.02, 0.02).

secondary_distortion_limit tuple[float, float] | float

Range of the secondary radial distortion coefficient. If a single float value is provided, the range will be (-secondary_distortion_limit, secondary_distortion_limit). This parameter controls the distortion in the corners of the image: - Positive values enhance pincushion distortion - Negative values enhance barrel distortion Default: (-0.05, 0.05).

mode Literal[\"green_purple\", \"red_blue\", \"random\"]

Type of color fringing to apply. Options are: - 'green_purple': Distorts red and blue channels in opposite directions, creating green-purple fringing. - 'red_blue': Distorts red and blue channels in the same direction, creating red-blue fringing. - 'random': Randomly chooses between 'green_purple' and 'red_blue' modes for each application. Default: 'green_purple'.

interpolation InterpolationType

Flag specifying the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • This transform only affects RGB images. Grayscale images will raise an error.
  • The strength of the effect depends on both primary and secondary distortion limits.
  • Higher absolute values for distortion limits will result in more pronounced chromatic aberration.
  • The 'green_purple' mode tends to produce more noticeable effects than 'red_blue'.

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> transform = A.ChromaticAberration(\n...     primary_distortion_limit=0.05,\n...     secondary_distortion_limit=0.1,\n...     mode='green_purple',\n...     interpolation=cv2.INTER_LINEAR,\n...     p=1.0\n... )\n>>> transformed = transform(image=image)\n>>> aberrated_image = transformed['image']\n

References

  • https://en.wikipedia.org/wiki/Chromatic_aberration
  • https://www.researchgate.net/publication/320691320_Chromatic_Aberration_in_Digital_Images

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ChromaticAberration(ImageOnlyTransform):\n    \"\"\"Add lateral chromatic aberration by distorting the red and blue channels of the input image.\n\n    Chromatic aberration is an optical effect that occurs when a lens fails to focus all colors to the same point.\n    This transform simulates this effect by applying different radial distortions to the red and blue channels\n    of the image, while leaving the green channel unchanged.\n\n    Args:\n        primary_distortion_limit (tuple[float, float] | float): Range of the primary radial distortion coefficient.\n            If a single float value is provided, the range\n            will be (-primary_distortion_limit, primary_distortion_limit).\n            This parameter controls the distortion in the center of the image:\n            - Positive values result in pincushion distortion (edges bend inward)\n            - Negative values result in barrel distortion (edges bend outward)\n            Default: (-0.02, 0.02).\n\n        secondary_distortion_limit (tuple[float, float] | float): Range of the secondary radial distortion coefficient.\n            If a single float value is provided, the range\n            will be (-secondary_distortion_limit, secondary_distortion_limit).\n            This parameter controls the distortion in the corners of the image:\n            - Positive values enhance pincushion distortion\n            - Negative values enhance barrel distortion\n            Default: (-0.05, 0.05).\n\n        mode (Literal[\"green_purple\", \"red_blue\", \"random\"]): Type of color fringing to apply. Options are:\n            - 'green_purple': Distorts red and blue channels in opposite directions, creating green-purple fringing.\n            - 'red_blue': Distorts red and blue channels in the same direction, creating red-blue fringing.\n            - 'random': Randomly chooses between 'green_purple' and 'red_blue' modes for each application.\n            Default: 'green_purple'.\n\n        interpolation (InterpolationType): Flag specifying the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - This transform only affects RGB images. Grayscale images will raise an error.\n        - The strength of the effect depends on both primary and secondary distortion limits.\n        - Higher absolute values for distortion limits will result in more pronounced chromatic aberration.\n        - The 'green_purple' mode tends to produce more noticeable effects than 'red_blue'.\n\n    Example:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> transform = A.ChromaticAberration(\n        ...     primary_distortion_limit=0.05,\n        ...     secondary_distortion_limit=0.1,\n        ...     mode='green_purple',\n        ...     interpolation=cv2.INTER_LINEAR,\n        ...     p=1.0\n        ... )\n        >>> transformed = transform(image=image)\n        >>> aberrated_image = transformed['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Chromatic_aberration\n        - https://www.researchgate.net/publication/320691320_Chromatic_Aberration_in_Digital_Images\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        primary_distortion_limit: SymmetricRangeType\n        secondary_distortion_limit: SymmetricRangeType\n        mode: ChromaticAberrationMode\n        interpolation: InterpolationType\n\n    def __init__(\n        self,\n        primary_distortion_limit: ScaleFloatType = (-0.02, 0.02),\n        secondary_distortion_limit: ScaleFloatType = (-0.05, 0.05),\n        mode: ChromaticAberrationMode = \"green_purple\",\n        interpolation: InterpolationType = cv2.INTER_LINEAR,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.primary_distortion_limit = cast(\n            tuple[float, float],\n            primary_distortion_limit,\n        )\n        self.secondary_distortion_limit = cast(\n            tuple[float, float],\n            secondary_distortion_limit,\n        )\n        self.mode = mode\n        self.interpolation = interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        primary_distortion_red: float,\n        secondary_distortion_red: float,\n        primary_distortion_blue: float,\n        secondary_distortion_blue: float,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.chromatic_aberration(\n            img,\n            primary_distortion_red,\n            secondary_distortion_red,\n            primary_distortion_blue,\n            secondary_distortion_blue,\n            self.interpolation,\n        )\n\n    def get_params(self) -> dict[str, float]:\n        primary_distortion_red = self.py_random.uniform(*self.primary_distortion_limit)\n        secondary_distortion_red = self.py_random.uniform(\n            *self.secondary_distortion_limit,\n        )\n        primary_distortion_blue = self.py_random.uniform(*self.primary_distortion_limit)\n        secondary_distortion_blue = self.py_random.uniform(\n            *self.secondary_distortion_limit,\n        )\n\n        secondary_distortion_red = self._match_sign(\n            primary_distortion_red,\n            secondary_distortion_red,\n        )\n        secondary_distortion_blue = self._match_sign(\n            primary_distortion_blue,\n            secondary_distortion_blue,\n        )\n\n        if self.mode == \"green_purple\":\n            # distortion coefficients of the red and blue channels have the same sign\n            primary_distortion_blue = self._match_sign(\n                primary_distortion_red,\n                primary_distortion_blue,\n            )\n            secondary_distortion_blue = self._match_sign(\n                secondary_distortion_red,\n                secondary_distortion_blue,\n            )\n        if self.mode == \"red_blue\":\n            # distortion coefficients of the red and blue channels have the opposite sign\n            primary_distortion_blue = self._unmatch_sign(\n                primary_distortion_red,\n                primary_distortion_blue,\n            )\n            secondary_distortion_blue = self._unmatch_sign(\n                secondary_distortion_red,\n                secondary_distortion_blue,\n            )\n\n        return {\n            \"primary_distortion_red\": primary_distortion_red,\n            \"secondary_distortion_red\": secondary_distortion_red,\n            \"primary_distortion_blue\": primary_distortion_blue,\n            \"secondary_distortion_blue\": secondary_distortion_blue,\n        }\n\n    @staticmethod\n    def _match_sign(a: float, b: float) -> float:\n        # Match the sign of b to a\n        if (a < 0 < b) or (a > 0 > b):\n            return -b\n        return b\n\n    @staticmethod\n    def _unmatch_sign(a: float, b: float) -> float:\n        # Unmatch the sign of b to a\n        if (a < 0 and b < 0) or (a > 0 and b > 0):\n            return -b\n        return b\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return (\n            \"primary_distortion_limit\",\n            \"secondary_distortion_limit\",\n            \"mode\",\n            \"interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ColorJitter","title":"class ColorJitter (brightness=(0.8, 1.2), contrast=(0.8, 1.2), saturation=(0.8, 1.2), hue=(-0.5, 0.5), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly changes the brightness, contrast, saturation, and hue of an image.

This transform is similar to torchvision's ColorJitter but with some differences due to the use of OpenCV instead of Pillow. The main differences are: 1. OpenCV and Pillow use different formulas to convert images to HSV format. 2. This implementation uses value saturation instead of uint8 overflow as in Pillow.

These differences may result in slightly different output compared to torchvision's ColorJitter.

Parameters:

Name Type Description brightness tuple[float, float] | float

How much to jitter brightness. If float: The brightness factor is chosen uniformly from [max(0, 1 - brightness), 1 + brightness]. If tuple: The brightness factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

contrast tuple[float, float] | float

How much to jitter contrast. If float: The contrast factor is chosen uniformly from [max(0, 1 - contrast), 1 + contrast]. If tuple: The contrast factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

saturation tuple[float, float] | float

How much to jitter saturation. If float: The saturation factor is chosen uniformly from [max(0, 1 - saturation), 1 + saturation]. If tuple: The saturation factor is sampled from the range specified. Should be non-negative numbers. Default: (0.8, 1.2)

hue float or tuple of float (min, max

How much to jitter hue. If float: The hue factor is chosen uniformly from [-hue, hue]. Should have 0 <= hue <= 0.5. If tuple: The hue factor is sampled from the range specified. Values should be in range [-0.5, 0.5]. Default: (-0.5, 0.5)

p (float): Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: 1, 3

Note

  • The order of application for these color transformations is random for each image.
  • The ranges for brightness, contrast, and saturation are applied as multiplicative factors.
  • The range for hue is applied as an additive factor.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> jittered_image = result['image']\n

References

  • https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.ColorJitter
  • https://docs.opencv.org/3.4/de/d25/imgproc_color_conversions.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ColorJitter(ImageOnlyTransform):\n    \"\"\"Randomly changes the brightness, contrast, saturation, and hue of an image.\n\n    This transform is similar to torchvision's ColorJitter but with some differences due to the use of OpenCV\n    instead of Pillow. The main differences are:\n    1. OpenCV and Pillow use different formulas to convert images to HSV format.\n    2. This implementation uses value saturation instead of uint8 overflow as in Pillow.\n\n    These differences may result in slightly different output compared to torchvision's ColorJitter.\n\n    Args:\n        brightness (tuple[float, float] | float): How much to jitter brightness.\n            If float:\n                The brightness factor is chosen uniformly from [max(0, 1 - brightness), 1 + brightness].\n            If tuple:\n                The brightness factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        contrast (tuple[float, float] | float): How much to jitter contrast.\n            If float:\n                The contrast factor is chosen uniformly from [max(0, 1 - contrast), 1 + contrast].\n            If tuple:\n                The contrast factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        saturation (tuple[float, float] | float): How much to jitter saturation.\n            If float:\n                The saturation factor is chosen uniformly from [max(0, 1 - saturation), 1 + saturation].\n            If tuple:\n                The saturation factor is sampled from the range specified.\n            Should be non-negative numbers.\n            Default: (0.8, 1.2)\n\n        hue (float or tuple of float (min, max)): How much to jitter hue.\n            If float:\n                The hue factor is chosen uniformly from [-hue, hue]. Should have 0 <= hue <= 0.5.\n            If tuple:\n                The hue factor is sampled from the range specified. Values should be in range [-0.5, 0.5].\n            Default: (-0.5, 0.5)\n\n         p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1, 3\n\n    Note:\n        - The order of application for these color transformations is random for each image.\n        - The ranges for brightness, contrast, and saturation are applied as multiplicative factors.\n        - The range for hue is applied as an additive factor.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> jittered_image = result['image']\n\n    References:\n        - https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.ColorJitter\n        - https://docs.opencv.org/3.4/de/d25/imgproc_color_conversions.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness: ScaleFloatType\n        contrast: ScaleFloatType\n        saturation: ScaleFloatType\n        hue: ScaleFloatType\n\n        @field_validator(\"brightness\", \"contrast\", \"saturation\", \"hue\")\n        @classmethod\n        def check_ranges(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            if info.field_name == \"hue\":\n                bounds = -0.5, 0.5\n                bias = 0\n                clip = False\n            elif info.field_name in [\"brightness\", \"contrast\", \"saturation\"]:\n                bounds = 0, float(\"inf\")\n                bias = 1\n                clip = True\n\n            if isinstance(value, numbers.Number):\n                if value < 0:\n                    raise ValueError(\n                        f\"If {info.field_name} is a single number, it must be non negative.\",\n                    )\n                left = bias - value\n                if clip:\n                    left = max(left, 0)\n                value = (left, bias + value)\n            elif isinstance(value, tuple) and len(value) == PAIR:\n                check_range(value, *bounds, info.field_name)\n\n            return cast(tuple[float, float], value)\n\n    def __init__(\n        self,\n        brightness: ScaleFloatType = (0.8, 1.2),\n        contrast: ScaleFloatType = (0.8, 1.2),\n        saturation: ScaleFloatType = (0.8, 1.2),\n        hue: ScaleFloatType = (-0.5, 0.5),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.brightness = cast(tuple[float, float], brightness)\n        self.contrast = cast(tuple[float, float], contrast)\n        self.saturation = cast(tuple[float, float], saturation)\n        self.hue = cast(tuple[float, float], hue)\n\n        self.transforms = [\n            fmain.adjust_brightness_torchvision,\n            fmain.adjust_contrast_torchvision,\n            fmain.adjust_saturation_torchvision,\n            fmain.adjust_hue_torchvision,\n        ]\n\n    def get_params(self) -> dict[str, Any]:\n        brightness = self.py_random.uniform(*self.brightness)\n        contrast = self.py_random.uniform(*self.contrast)\n        saturation = self.py_random.uniform(*self.saturation)\n        hue = self.py_random.uniform(*self.hue)\n\n        order = [0, 1, 2, 3]\n        self.random_generator.shuffle(order)\n\n        return {\n            \"brightness\": brightness,\n            \"contrast\": contrast,\n            \"saturation\": saturation,\n            \"hue\": hue,\n            \"order\": order,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        brightness: float,\n        contrast: float,\n        saturation: float,\n        hue: float,\n        order: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"ColorJitter transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n        color_transforms = [brightness, contrast, saturation, hue]\n        for i in order:\n            img = self.transforms[i](img, color_transforms[i])\n        return img\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"brightness\", \"contrast\", \"saturation\", \"hue\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Downscale","title":"class Downscale (scale_min=None, scale_max=None, interpolation=None, scale_range=(0.25, 0.25), interpolation_pair={'upscale': 0, 'downscale': 0}, always_apply=None, p=0.5) [view source on GitHub]","text":"

Decrease image quality by downscaling and upscaling back.

This transform simulates the effect of a low-resolution image by first downscaling the image to a lower resolution and then upscaling it back to its original size. This process introduces loss of detail and can be used to simulate low-quality images or to test the robustness of models to different image resolutions.

Parameters:

Name Type Description scale_range tuple[float, float]

Range for the downscaling factor. Should be two float values between 0 and 1, where the first value is less than or equal to the second. The actual downscaling factor will be randomly chosen from this range for each image. Lower values result in more aggressive downscaling. Default: (0.25, 0.25)

interpolation_pair InterpolationDict

A dictionary specifying the interpolation methods to use for downscaling and upscaling. Should contain two keys: - 'downscale': Interpolation method for downscaling - 'upscale': Interpolation method for upscaling Values should be OpenCV interpolation flags (e.g., cv2.INTER_NEAREST, cv2.INTER_LINEAR, etc.) Default: {'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_NEAREST}

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Note

  • The actual downscaling factor is randomly chosen for each image from the range specified in scale_range.
  • Using different interpolation methods for downscaling and upscaling can produce various effects. For example, using INTER_NEAREST for both can create a pixelated look, while using INTER_LINEAR or INTER_CUBIC can produce smoother results.
  • This transform can be useful for data augmentation, especially when training models that need to be robust to variations in image quality or resolution.

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> transform = A.Downscale(\n...     scale_range=(0.5, 0.75),\n...     interpolation_pair={'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_LINEAR},\n...     p=0.5\n... )\n>>> transformed = transform(image=image)\n>>> downscaled_image = transformed['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Downscale(ImageOnlyTransform):\n    \"\"\"Decrease image quality by downscaling and upscaling back.\n\n    This transform simulates the effect of a low-resolution image by first downscaling\n    the image to a lower resolution and then upscaling it back to its original size.\n    This process introduces loss of detail and can be used to simulate low-quality\n    images or to test the robustness of models to different image resolutions.\n\n    Args:\n        scale_range (tuple[float, float]): Range for the downscaling factor.\n            Should be two float values between 0 and 1, where the first value is less than or equal to the second.\n            The actual downscaling factor will be randomly chosen from this range for each image.\n            Lower values result in more aggressive downscaling.\n            Default: (0.25, 0.25)\n\n        interpolation_pair (InterpolationDict): A dictionary specifying the interpolation methods to use for\n            downscaling and upscaling. Should contain two keys:\n            - 'downscale': Interpolation method for downscaling\n            - 'upscale': Interpolation method for upscaling\n            Values should be OpenCV interpolation flags (e.g., cv2.INTER_NEAREST, cv2.INTER_LINEAR, etc.)\n            Default: {'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_NEAREST}\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual downscaling factor is randomly chosen for each image from the range\n          specified in scale_range.\n        - Using different interpolation methods for downscaling and upscaling can produce\n          various effects. For example, using INTER_NEAREST for both can create a pixelated look,\n          while using INTER_LINEAR or INTER_CUBIC can produce smoother results.\n        - This transform can be useful for data augmentation, especially when training models\n          that need to be robust to variations in image quality or resolution.\n\n    Example:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> transform = A.Downscale(\n        ...     scale_range=(0.5, 0.75),\n        ...     interpolation_pair={'downscale': cv2.INTER_NEAREST, 'upscale': cv2.INTER_LINEAR},\n        ...     p=0.5\n        ... )\n        >>> transformed = transform(image=image)\n        >>> downscaled_image = transformed['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_min: float | None\n        scale_max: float | None\n\n        interpolation: int | Interpolation | InterpolationDict | None = Field(\n            default_factory=lambda: Interpolation(\n                downscale=cv2.INTER_NEAREST,\n                upscale=cv2.INTER_NEAREST,\n            ),\n        )\n        interpolation_pair: InterpolationPydantic\n\n        scale_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_params(self) -> Self:\n            if self.scale_min is not None and self.scale_max is not None:\n                warn(\n                    \"scale_min and scale_max are deprecated. Use scale_range instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n                self.scale_range = (self.scale_min, self.scale_max)\n                self.scale_min = None\n                self.scale_max = None\n\n            if self.interpolation is not None:\n                warn(\n                    \"Downscale.interpolation is deprecated. Use Downscale.interpolation_pair instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n                if isinstance(self.interpolation, dict):\n                    self.interpolation_pair = InterpolationPydantic(\n                        **self.interpolation,\n                    )\n                elif isinstance(self.interpolation, int):\n                    self.interpolation_pair = InterpolationPydantic(\n                        upscale=self.interpolation,\n                        downscale=self.interpolation,\n                    )\n                elif isinstance(self.interpolation, Interpolation):\n                    self.interpolation_pair = InterpolationPydantic(\n                        upscale=self.interpolation.upscale,\n                        downscale=self.interpolation.downscale,\n                    )\n                self.interpolation = None\n\n            return self\n\n    def __init__(\n        self,\n        scale_min: float | None = None,\n        scale_max: float | None = None,\n        interpolation: int | Interpolation | InterpolationDict | None = None,\n        scale_range: tuple[float, float] = (0.25, 0.25),\n        interpolation_pair: InterpolationDict = InterpolationDict(\n            {\"upscale\": cv2.INTER_NEAREST, \"downscale\": cv2.INTER_NEAREST},\n        ),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_range = scale_range\n        self.interpolation_pair = interpolation_pair\n\n    def apply(self, img: np.ndarray, scale: float, **params: Any) -> np.ndarray:\n        return fmain.downscale(\n            img,\n            scale=scale,\n            down_interpolation=self.interpolation_pair[\"downscale\"],\n            up_interpolation=self.interpolation_pair[\"upscale\"],\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\"scale\": self.py_random.uniform(*self.scale_range)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"scale_range\", \"interpolation_pair\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Emboss","title":"class Emboss (alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply embossing effect to the input image.

This transform creates an emboss effect by highlighting edges and creating a 3D-like texture in the image. It works by applying a specific convolution kernel to the image that emphasizes differences in adjacent pixel values.

Parameters:

Name Type Description alpha tuple[float, float]

Range to choose the visibility of the embossed image. At 0, only the original image is visible, at 1.0 only its embossed version is visible. Values should be in the range [0, 1]. Alpha will be randomly selected from this range for each image. Default: (0.2, 0.5)

strength tuple[float, float]

Range to choose the strength of the embossing effect. Higher values create a more pronounced 3D effect. Values should be non-negative. Strength will be randomly selected from this range for each image. Default: (0.2, 0.7)

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Note

  • The emboss effect is created using a 3x3 convolution kernel.
  • The 'alpha' parameter controls the blend between the original image and the embossed version. A higher alpha value will result in a more pronounced emboss effect.
  • The 'strength' parameter affects the intensity of the embossing. Higher strength values will create more contrast in the embossed areas, resulting in a stronger 3D-like effect.
  • This transform can be useful for creating artistic effects or for data augmentation in tasks where edge information is important.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Emboss(alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5)\n>>> result = transform(image=image)\n>>> embossed_image = result['image']\n

References

  • https://en.wikipedia.org/wiki/Image_embossing
  • https://www.researchgate.net/publication/303412455_Application_of_Emboss_Filtering_in_Image_Processing

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Emboss(ImageOnlyTransform):\n    \"\"\"Apply embossing effect to the input image.\n\n    This transform creates an emboss effect by highlighting edges and creating a 3D-like texture\n    in the image. It works by applying a specific convolution kernel to the image that emphasizes\n    differences in adjacent pixel values.\n\n    Args:\n        alpha (tuple[float, float]): Range to choose the visibility of the embossed image.\n            At 0, only the original image is visible, at 1.0 only its embossed version is visible.\n            Values should be in the range [0, 1].\n            Alpha will be randomly selected from this range for each image.\n            Default: (0.2, 0.5)\n\n        strength (tuple[float, float]): Range to choose the strength of the embossing effect.\n            Higher values create a more pronounced 3D effect.\n            Values should be non-negative.\n            Strength will be randomly selected from this range for each image.\n            Default: (0.2, 0.7)\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The emboss effect is created using a 3x3 convolution kernel.\n        - The 'alpha' parameter controls the blend between the original image and the embossed version.\n          A higher alpha value will result in a more pronounced emboss effect.\n        - The 'strength' parameter affects the intensity of the embossing. Higher strength values\n          will create more contrast in the embossed areas, resulting in a stronger 3D-like effect.\n        - This transform can be useful for creating artistic effects or for data augmentation\n          in tasks where edge information is important.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Emboss(alpha=(0.2, 0.5), strength=(0.2, 0.7), p=0.5)\n        >>> result = transform(image=image)\n        >>> embossed_image = result['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Image_embossing\n        - https://www.researchgate.net/publication/303412455_Application_of_Emboss_Filtering_in_Image_Processing\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        strength: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, None))]\n\n    def __init__(\n        self,\n        alpha: tuple[float, float] = (0.2, 0.5),\n        strength: tuple[float, float] = (0.2, 0.7),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n        self.strength = strength\n\n    @staticmethod\n    def __generate_emboss_matrix(\n        alpha_sample: np.ndarray,\n        strength_sample: np.ndarray,\n    ) -> np.ndarray:\n        matrix_nochange = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=np.float32)\n        matrix_effect = np.array(\n            [\n                [-1 - strength_sample, 0 - strength_sample, 0],\n                [0 - strength_sample, 1, 0 + strength_sample],\n                [0, 0 + strength_sample, 1 + strength_sample],\n            ],\n            dtype=np.float32,\n        )\n        return (1 - alpha_sample) * matrix_nochange + alpha_sample * matrix_effect\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        alpha = self.py_random.uniform(*self.alpha)\n        strength = self.py_random.uniform(*self.strength)\n        emboss_matrix = self.__generate_emboss_matrix(\n            alpha_sample=alpha,\n            strength_sample=strength,\n        )\n        return {\"emboss_matrix\": emboss_matrix}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        emboss_matrix: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.convolve(img, emboss_matrix)\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"alpha\", \"strength\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Equalize","title":"class Equalize (mode='cv', by_channels=True, mask=None, mask_params=(), always_apply=None, p=0.5) [view source on GitHub]","text":"

Equalize the image histogram.

This transform applies histogram equalization to the input image. Histogram equalization is a method in image processing of contrast adjustment using the image's histogram.

Parameters:

Name Type Description mode Literal['cv', 'pil']

Use OpenCV or Pillow equalization method. Default: 'cv'

by_channels bool

If True, use equalization by channels separately, else convert image to YCbCr representation and use equalization by Y channel. Default: True

mask np.ndarray, callable

If given, only the pixels selected by the mask are included in the analysis. Can be: - A 1-channel or 3-channel numpy array of the same size as the input image. - A callable (function) that generates a mask. The function should accept 'image' as its first argument, and can accept additional arguments specified in mask_params. Default: None

mask_params list[str]

Additional parameters to pass to the mask function. These parameters will be taken from the data dict passed to call. Default: ()

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Note

  • When mode='cv', OpenCV's equalizeHist() function is used.
  • When mode='pil', Pillow's equalize() function is used.
  • The 'by_channels' parameter determines whether equalization is applied to each color channel independently (True) or to the luminance channel only (False).
  • If a mask is provided as a numpy array, it should have the same height and width as the input image.
  • If a mask is provided as a function, it allows for dynamic mask generation based on the input image and additional parameters. This is useful for scenarios where the mask depends on the image content or external data (e.g., bounding boxes, segmentation masks).

Mask Function: When mask is a callable, it should have the following signature: mask_func(image, *args) -> np.ndarray

- image: The input image (numpy array)\n- *args: Additional arguments as specified in mask_params\n\nThe function should return a numpy array of the same height and width as the input image,\nwhere non-zero pixels indicate areas to be equalized.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>>\n>>> # Using a static mask\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> transform = A.Equalize(mask=mask, p=1.0)\n>>> result = transform(image=image)\n>>>\n>>> # Using a dynamic mask function\n>>> def mask_func(image, bboxes):\n...     mask = np.ones_like(image[:, :, 0], dtype=np.uint8)\n...     for bbox in bboxes:\n...         x1, y1, x2, y2 = map(int, bbox)\n...         mask[y1:y2, x1:x2] = 0  # Exclude areas inside bounding boxes\n...     return mask\n>>>\n>>> transform = A.Equalize(mask=mask_func, mask_params=['bboxes'], p=1.0)\n>>> bboxes = [(10, 10, 50, 50), (60, 60, 90, 90)]  # Example bounding boxes\n>>> result = transform(image=image, bboxes=bboxes)\n

References

  • OpenCV equalizeHist: https://docs.opencv.org/3.4/d6/dc7/group__imgproc__hist.html#ga7e54091f0c937d49bf84152a16f76d6e
  • Pillow ImageOps.equalize: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.equalize
  • Histogram Equalization: https://en.wikipedia.org/wiki/Histogram_equalization

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Equalize(ImageOnlyTransform):\n    \"\"\"Equalize the image histogram.\n\n    This transform applies histogram equalization to the input image. Histogram equalization\n    is a method in image processing of contrast adjustment using the image's histogram.\n\n    Args:\n        mode (Literal['cv', 'pil']): Use OpenCV or Pillow equalization method.\n            Default: 'cv'\n        by_channels (bool): If True, use equalization by channels separately,\n            else convert image to YCbCr representation and use equalization by `Y` channel.\n            Default: True\n        mask (np.ndarray, callable): If given, only the pixels selected by\n            the mask are included in the analysis. Can be:\n            - A 1-channel or 3-channel numpy array of the same size as the input image.\n            - A callable (function) that generates a mask. The function should accept 'image'\n              as its first argument, and can accept additional arguments specified in mask_params.\n            Default: None\n        mask_params (list[str]): Additional parameters to pass to the mask function.\n            These parameters will be taken from the data dict passed to __call__.\n            Default: ()\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - When mode='cv', OpenCV's equalizeHist() function is used.\n        - When mode='pil', Pillow's equalize() function is used.\n        - The 'by_channels' parameter determines whether equalization is applied to each color channel\n          independently (True) or to the luminance channel only (False).\n        - If a mask is provided as a numpy array, it should have the same height and width as the input image.\n        - If a mask is provided as a function, it allows for dynamic mask generation based on the input image\n          and additional parameters. This is useful for scenarios where the mask depends on the image content\n          or external data (e.g., bounding boxes, segmentation masks).\n\n    Mask Function:\n        When mask is a callable, it should have the following signature:\n        mask_func(image, *args) -> np.ndarray\n\n        - image: The input image (numpy array)\n        - *args: Additional arguments as specified in mask_params\n\n        The function should return a numpy array of the same height and width as the input image,\n        where non-zero pixels indicate areas to be equalized.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>>\n        >>> # Using a static mask\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> transform = A.Equalize(mask=mask, p=1.0)\n        >>> result = transform(image=image)\n        >>>\n        >>> # Using a dynamic mask function\n        >>> def mask_func(image, bboxes):\n        ...     mask = np.ones_like(image[:, :, 0], dtype=np.uint8)\n        ...     for bbox in bboxes:\n        ...         x1, y1, x2, y2 = map(int, bbox)\n        ...         mask[y1:y2, x1:x2] = 0  # Exclude areas inside bounding boxes\n        ...     return mask\n        >>>\n        >>> transform = A.Equalize(mask=mask_func, mask_params=['bboxes'], p=1.0)\n        >>> bboxes = [(10, 10, 50, 50), (60, 60, 90, 90)]  # Example bounding boxes\n        >>> result = transform(image=image, bboxes=bboxes)\n\n    References:\n        - OpenCV equalizeHist: https://docs.opencv.org/3.4/d6/dc7/group__imgproc__hist.html#ga7e54091f0c937d49bf84152a16f76d6e\n        - Pillow ImageOps.equalize: https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.equalize\n        - Histogram Equalization: https://en.wikipedia.org/wiki/Histogram_equalization\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: ImageMode\n        by_channels: bool\n        mask: np.ndarray | Callable[..., Any] | None\n        mask_params: Sequence[str]\n\n    def __init__(\n        self,\n        mode: ImageMode = \"cv\",\n        by_channels: bool = True,\n        mask: np.ndarray | Callable[..., Any] | None = None,\n        mask_params: Sequence[str] = (),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.mode = mode\n        self.by_channels = by_channels\n        self.mask = mask\n        self.mask_params = mask_params\n\n    def apply(self, img: np.ndarray, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.equalize(\n            img,\n            mode=self.mode,\n            by_channels=self.by_channels,\n            mask=mask,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if not callable(self.mask):\n            return {\"mask\": self.mask}\n\n        mask_params = {\"image\": data[\"image\"]}\n        for key in self.mask_params:\n            if key not in data:\n                raise KeyError(\n                    f\"Required parameter '{key}' for mask function is missing in data.\",\n                )\n            mask_params[key] = data[key]\n\n        return {\"mask\": self.mask(**mask_params)}\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [*list(self.mask_params)]\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mode\", \"by_channels\", \"mask\", \"mask_params\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.FancyPCA","title":"class FancyPCA (alpha=0.1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Fancy PCA augmentation to the input image.

This augmentation technique applies PCA (Principal Component Analysis) to the image's color channels, then adds multiples of the principal components to the image, with magnitudes proportional to the corresponding eigenvalues times a random variable drawn from a Gaussian with mean 0 and standard deviation 'alpha'.

Parameters:

Name Type Description alpha tuple[float, float] | float

Standard deviation of the Gaussian distribution used to generate random noise for each principal component. If a single float is provided, it will be used for all channels. If a tuple of two floats (min, max) is provided, the standard deviation will be uniformly sampled from this range for each run. Default: 0.1.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: any

Note

  • This augmentation is particularly effective for RGB images but can work with any number of channels.
  • For grayscale images, it applies a simplified version of the augmentation.
  • The transform preserves the mean of the image while adjusting the color/intensity variation.
  • This implementation is based on the paper by Krizhevsky et al. and is similar to the one used in the original AlexNet paper.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.FancyPCA(alpha=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> augmented_image = result[\"image\"]\n

References

  • Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).
  • https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class FancyPCA(ImageOnlyTransform):\n    \"\"\"Apply Fancy PCA augmentation to the input image.\n\n    This augmentation technique applies PCA (Principal Component Analysis) to the image's color channels,\n    then adds multiples of the principal components to the image, with magnitudes proportional to the\n    corresponding eigenvalues times a random variable drawn from a Gaussian with mean 0 and standard\n    deviation 'alpha'.\n\n    Args:\n        alpha (tuple[float, float] | float): Standard deviation of the Gaussian distribution used to generate\n            random noise for each principal component. If a single float is provided, it will be used for\n            all channels. If a tuple of two floats (min, max) is provided, the standard deviation will be\n            uniformly sampled from this range for each run. Default: 0.1.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        any\n\n    Note:\n        - This augmentation is particularly effective for RGB images but can work with any number of channels.\n        - For grayscale images, it applies a simplified version of the augmentation.\n        - The transform preserves the mean of the image while adjusting the color/intensity variation.\n        - This implementation is based on the paper by Krizhevsky et al. and is similar to the one used\n          in the original AlexNet paper.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.FancyPCA(alpha=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> augmented_image = result[\"image\"]\n\n    References:\n        - Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). ImageNet classification with deep\n          convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).\n        - https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: float = Field(ge=0)\n\n    def __init__(\n        self,\n        alpha: float = 0.1,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha_vector: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.fancy_pca(img, alpha_vector)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        shape = params[\"shape\"]\n        num_channels = shape[-1] if len(shape) == NUM_MULTI_CHANNEL_DIMENSIONS else 1\n        alpha_vector = self.random_generator.normal(0, self.alpha, num_channels).astype(\n            np.float32,\n        )\n        return {\"alpha_vector\": alpha_vector}\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"alpha\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.FromFloat","title":"class FromFloat (dtype='uint8', max_value=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

Convert an image from floating point representation to the specified data type.

This transform is designed to convert images from a normalized floating-point representation (typically with values in the range [0, 1]) to other data types, scaling the values appropriately.

Parameters:

Name Type Description dtype str

The desired output data type. Supported types include 'uint8', 'uint16', 'uint32'. Default: 'uint8'.

max_value float | None

The maximum value for the output dtype. If None, the transform will attempt to infer the maximum value based on the dtype. Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: float32, float64

Note

  • This is the inverse transform for ToFloat.
  • Input images are expected to be in floating point format with values in the range [0, 1].
  • For integer output types (uint8, uint16, uint32), the function will scale the values to the appropriate range (e.g., 0-255 for uint8).
  • For float output types (float32, float64), the values will remain in the [0, 1] range.
  • The transform uses the from_float function internally, which ensures output values are within the valid range for the specified dtype.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> transform = A.FromFloat(dtype='uint8', max_value=None, p=1.0)\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)  # Float image in [0, 1] range\n>>> result = transform(image=image)\n>>> uint8_image = result['image']\n>>> assert uint8_image.dtype == np.uint8\n>>> assert uint8_image.min() >= 0 and uint8_image.max() <= 255\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class FromFloat(ImageOnlyTransform):\n    \"\"\"Convert an image from floating point representation to the specified data type.\n\n    This transform is designed to convert images from a normalized floating-point representation\n    (typically with values in the range [0, 1]) to other data types, scaling the values appropriately.\n\n    Args:\n        dtype (str): The desired output data type. Supported types include 'uint8', 'uint16',\n                     'uint32'. Default: 'uint8'.\n        max_value (float | None): The maximum value for the output dtype. If None, the transform\n                                  will attempt to infer the maximum value based on the dtype.\n                                  Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        float32, float64\n\n    Note:\n        - This is the inverse transform for ToFloat.\n        - Input images are expected to be in floating point format with values in the range [0, 1].\n        - For integer output types (uint8, uint16, uint32), the function will scale the values\n          to the appropriate range (e.g., 0-255 for uint8).\n        - For float output types (float32, float64), the values will remain in the [0, 1] range.\n        - The transform uses the `from_float` function internally, which ensures output values\n          are within the valid range for the specified dtype.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> transform = A.FromFloat(dtype='uint8', max_value=None, p=1.0)\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)  # Float image in [0, 1] range\n        >>> result = transform(image=image)\n        >>> uint8_image = result['image']\n        >>> assert uint8_image.dtype == np.uint8\n        >>> assert uint8_image.min() >= 0 and uint8_image.max() <= 255\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        dtype: Literal[\"uint8\", \"uint16\", \"float32\", \"float64\"]\n        max_value: float | None\n\n    def __init__(\n        self,\n        dtype: Literal[\"uint8\", \"uint16\", \"float32\", \"float64\"] = \"uint8\",\n        max_value: float | None = None,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.dtype = np.dtype(dtype)\n        self.max_value = max_value\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return from_float(img, self.dtype, self.max_value)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\"dtype\": self.dtype.name, \"max_value\": self.max_value}\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GaussNoise","title":"class GaussNoise (var_limit=None, mean=None, std_range=(0.2, 0.44), mean_range=(0.0, 0.0), per_channel=True, noise_scale_factor=1, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Gaussian noise to the input image.

Parameters:

Name Type Description std_range tuple[float, float]

Range for noise standard deviation as a fraction of the maximum value (255 for uint8 images or 1.0 for float images). Values should be in range [0, 1]. Default: (0.2, 0.44).

mean_range tuple[float, float]

Range for noise mean as a fraction of the maximum value (255 for uint8 images or 1.0 for float images). Values should be in range [-1, 1]. Default: (0.0, 0.0).

var_limit tuple[float, float] | float

[Deprecated] Variance range for noise. If var_limit is a single float value, the range will be (0, var_limit). Default: (10.0, 50.0).

mean float

[Deprecated] Mean of the noise. Default: 0.

per_channel bool

If True, noise will be sampled for each channel independently. Otherwise, the noise will be sampled once for all channels. Default: True.

noise_scale_factor float

Scaling factor for noise generation. Value should be in the range (0, 1]. When set to 1, noise is sampled for each pixel independently. If less, noise is sampled for a smaller size and resized to fit the shape of the image. Smaller values make the transform faster. Default: 1.0.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The noise parameters (std_range and mean_range) are normalized to [0, 1] range:
  • For uint8 images, they are multiplied by 255
  • For float32 images, they are used directly
  • The behavior differs between old and new parameters:
  • When using var_limit (deprecated): samples variance uniformly and takes sqrt to get std dev
  • When using std_range: samples standard deviation directly (aligned with torchvision/kornia)
  • Setting per_channel=False is faster but applies the same noise to all channels
  • The noise_scale_factor parameter allows for a trade-off between transform speed and noise granularity

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)\n>>>\n>>> # Apply Gaussian noise with normalized std_range\n>>> transform = A.GaussNoise(std_range=(0.1, 0.2), p=1.0)  # 10-20% of max value\n>>> noisy_image = transform(image=image)['image']\n>>>\n>>> # Using deprecated var_limit (will be converted to std_range)\n>>> transform = A.GaussNoise(var_limit=(50.0, 100.0), mean=10, p=1.0)\n>>> noisy_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class GaussNoise(ImageOnlyTransform):\n    \"\"\"Apply Gaussian noise to the input image.\n\n    Args:\n        std_range (tuple[float, float]): Range for noise standard deviation as a fraction\n            of the maximum value (255 for uint8 images or 1.0 for float images).\n            Values should be in range [0, 1]. Default: (0.2, 0.44).\n        mean_range (tuple[float, float]): Range for noise mean as a fraction\n            of the maximum value (255 for uint8 images or 1.0 for float images).\n            Values should be in range [-1, 1]. Default: (0.0, 0.0).\n        var_limit (tuple[float, float] | float): [Deprecated] Variance range for noise.\n            If var_limit is a single float value, the range will be (0, var_limit).\n            Default: (10.0, 50.0).\n        mean (float): [Deprecated] Mean of the noise. Default: 0.\n        per_channel (bool): If True, noise will be sampled for each channel independently.\n            Otherwise, the noise will be sampled once for all channels. Default: True.\n        noise_scale_factor (float): Scaling factor for noise generation. Value should be in the range (0, 1].\n            When set to 1, noise is sampled for each pixel independently. If less, noise is sampled for a smaller size\n            and resized to fit the shape of the image. Smaller values make the transform faster. Default: 1.0.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The noise parameters (std_range and mean_range) are normalized to [0, 1] range:\n          * For uint8 images, they are multiplied by 255\n          * For float32 images, they are used directly\n        - The behavior differs between old and new parameters:\n          * When using var_limit (deprecated): samples variance uniformly and takes sqrt to get std dev\n          * When using std_range: samples standard deviation directly (aligned with torchvision/kornia)\n        - Setting per_channel=False is faster but applies the same noise to all channels\n        - The noise_scale_factor parameter allows for a trade-off between transform speed and noise granularity\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8)\n        >>>\n        >>> # Apply Gaussian noise with normalized std_range\n        >>> transform = A.GaussNoise(std_range=(0.1, 0.2), p=1.0)  # 10-20% of max value\n        >>> noisy_image = transform(image=image)['image']\n        >>>\n        >>> # Using deprecated var_limit (will be converted to std_range)\n        >>> transform = A.GaussNoise(var_limit=(50.0, 100.0), mean=10, p=1.0)\n        >>> noisy_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        var_limit: ScaleFloatType | None\n        mean: float | None\n        std_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        mean_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        per_channel: bool\n        noise_scale_factor: float = Field(gt=0, le=1)\n\n        @model_validator(mode=\"after\")\n        def check_range(self) -> Self:\n            if self.var_limit is not None:\n                warnings.warn(\"`var_limit` deprecated. Use `std_range` instead.\", DeprecationWarning, stacklevel=2)\n                self.var_limit = to_tuple(self.var_limit, 0)\n                if self.var_limit[1] > 1:\n                    # Convert legacy uint8 variance to normalized std dev\n                    self.std_range = (math.sqrt(10 / 255), math.sqrt(50 / 255))\n                else:\n                    # Already normalized variance, convert to std dev\n                    self.std_range = (\n                        math.sqrt(self.var_limit[0]),\n                        math.sqrt(self.var_limit[1]),\n                    )\n\n            if self.mean is not None:\n                warn(\"`mean` deprecated. Use `mean_range` instead.\", DeprecationWarning, stacklevel=2)\n                if self.mean >= 1:\n                    # Convert legacy uint8 mean to normalized range\n                    self.mean_range = (self.mean / 255, self.mean / 255)\n                else:\n                    # Already normalized mean\n                    self.mean_range = (self.mean, self.mean)\n\n            return self\n\n    def __init__(\n        self,\n        var_limit: ScaleFloatType | None = None,\n        mean: float | None = None,\n        std_range: tuple[float, float] = (0.2, 0.44),  # sqrt(10 / 255), sqrt(50 / 255)\n        mean_range: tuple[float, float] = (0.0, 0.0),\n        per_channel: bool = True,\n        noise_scale_factor: float = 1,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.std_range = std_range\n        self.mean_range = mean_range\n        self.per_channel = per_channel\n        self.noise_scale_factor = noise_scale_factor\n\n        self.var_limit = var_limit\n\n    def apply(\n        self,\n        img: np.ndarray,\n        noise_map: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_noise(img, noise_map)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, float]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n\n        if self.var_limit is not None:\n            # Legacy behavior: sample variance uniformly then take sqrt\n            var = self.py_random.uniform(self.std_range[0] ** 2, self.std_range[1] ** 2)\n            sigma = math.sqrt(var)\n        else:\n            # New behavior: sample std dev directly (aligned with torchvision/kornia)\n            sigma = self.py_random.uniform(*self.std_range)\n\n        mean = self.py_random.uniform(*self.mean_range)\n\n        noise_map = fmain.generate_noise(\n            noise_type=\"gaussian\",\n            spatial_mode=\"per_pixel\" if self.per_channel else \"shared\",\n            shape=image.shape,\n            params={\"mean_range\": (mean, mean), \"std_range\": (sigma, sigma)},\n            max_value=max_value,\n            approximation=self.noise_scale_factor,\n            random_generator=self.random_generator,\n        )\n\n        return {\"noise_map\": noise_map}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"std_range\", \"mean_range\", \"per_channel\", \"noise_scale_factor\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.GaussianParams","title":"class GaussianParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class GaussianParams(NoiseParamsBase):\n    noise_type: Literal[\"gaussian\"] = \"gaussian\"\n    mean_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=-1, max_val=1)),\n    ]\n    std_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.HueSaturationValue","title":"class HueSaturationValue (hue_shift_limit=(-20, 20), sat_shift_limit=(-30, 30), val_shift_limit=(-20, 20), always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly change hue, saturation and value of the input image.

This transform adjusts the HSV (Hue, Saturation, Value) channels of an input RGB image. It allows for independent control over each channel, providing a wide range of color and brightness modifications.

Parameters:

Name Type Description hue_shift_limit float | tuple[float, float]

Range for changing hue. If a single float value is provided, the range will be (-hue_shift_limit, hue_shift_limit). Values should be in the range [-180, 180]. Default: (-20, 20).

sat_shift_limit float | tuple[float, float]

Range for changing saturation. If a single float value is provided, the range will be (-sat_shift_limit, sat_shift_limit). Values should be in the range [-255, 255]. Default: (-30, 30).

val_shift_limit float | tuple[float, float]

Range for changing value (brightness). If a single float value is provided, the range will be (-val_shift_limit, val_shift_limit). Values should be in the range [-255, 255]. Default: (-20, 20).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The transform first converts the input RGB image to the HSV color space.
  • Each channel (Hue, Saturation, Value) is adjusted independently.
  • Hue is circular, so it wraps around at 180 degrees.
  • For float32 images, the shift values are applied as percentages of the full range.
  • This transform is particularly useful for color augmentation and simulating different lighting conditions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.HueSaturationValue(\n...     hue_shift_limit=20,\n...     sat_shift_limit=30,\n...     val_shift_limit=20,\n...     p=0.7\n... )\n>>> result = transform(image=image)\n>>> augmented_image = result[\"image\"]\n

References

  • HSV color space: https://en.wikipedia.org/wiki/HSL_and_HSV

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class HueSaturationValue(ImageOnlyTransform):\n    \"\"\"Randomly change hue, saturation and value of the input image.\n\n    This transform adjusts the HSV (Hue, Saturation, Value) channels of an input RGB image.\n    It allows for independent control over each channel, providing a wide range of color\n    and brightness modifications.\n\n    Args:\n        hue_shift_limit (float | tuple[float, float]): Range for changing hue.\n            If a single float value is provided, the range will be (-hue_shift_limit, hue_shift_limit).\n            Values should be in the range [-180, 180]. Default: (-20, 20).\n\n        sat_shift_limit (float | tuple[float, float]): Range for changing saturation.\n            If a single float value is provided, the range will be (-sat_shift_limit, sat_shift_limit).\n            Values should be in the range [-255, 255]. Default: (-30, 30).\n\n        val_shift_limit (float | tuple[float, float]): Range for changing value (brightness).\n            If a single float value is provided, the range will be (-val_shift_limit, val_shift_limit).\n            Values should be in the range [-255, 255]. Default: (-20, 20).\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The transform first converts the input RGB image to the HSV color space.\n        - Each channel (Hue, Saturation, Value) is adjusted independently.\n        - Hue is circular, so it wraps around at 180 degrees.\n        - For float32 images, the shift values are applied as percentages of the full range.\n        - This transform is particularly useful for color augmentation and simulating\n          different lighting conditions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.HueSaturationValue(\n        ...     hue_shift_limit=20,\n        ...     sat_shift_limit=30,\n        ...     val_shift_limit=20,\n        ...     p=0.7\n        ... )\n        >>> result = transform(image=image)\n        >>> augmented_image = result[\"image\"]\n\n    References:\n        - HSV color space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        hue_shift_limit: SymmetricRangeType\n        sat_shift_limit: SymmetricRangeType\n        val_shift_limit: SymmetricRangeType\n\n    def __init__(\n        self,\n        hue_shift_limit: ScaleFloatType = (-20, 20),\n        sat_shift_limit: ScaleFloatType = (-30, 30),\n        val_shift_limit: ScaleFloatType = (-20, 20),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.hue_shift_limit = cast(tuple[float, float], hue_shift_limit)\n        self.sat_shift_limit = cast(tuple[float, float], sat_shift_limit)\n        self.val_shift_limit = cast(tuple[float, float], val_shift_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        hue_shift: int,\n        sat_shift: int,\n        val_shift: int,\n        **params: Any,\n    ) -> np.ndarray:\n        if not is_rgb_image(img) and not is_grayscale_image(img):\n            msg = \"HueSaturationValue transformation expects 1-channel or 3-channel images.\"\n            raise TypeError(msg)\n        return fmain.shift_hsv(img, hue_shift, sat_shift, val_shift)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"hue_shift\": self.py_random.uniform(*self.hue_shift_limit),\n            \"sat_shift\": self.py_random.uniform(*self.sat_shift_limit),\n            \"val_shift\": self.py_random.uniform(*self.val_shift_limit),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"hue_shift_limit\", \"sat_shift_limit\", \"val_shift_limit\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ISONoise","title":"class ISONoise (color_shift=(0.01, 0.05), intensity=(0.1, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies camera sensor noise to the input image, simulating high ISO settings.

This transform adds random noise to an image, mimicking the effect of using high ISO settings in digital photography. It simulates two main components of ISO noise: 1. Color noise: random shifts in color hue 2. Luminance noise: random variations in pixel intensity

Parameters:

Name Type Description color_shift tuple[float, float]

Range for changing color hue. Values should be in the range [0, 1], where 1 represents a full 360\u00b0 hue rotation. Default: (0.01, 0.05)

intensity tuple[float, float]

Range for the noise intensity. Higher values increase the strength of both color and luminance noise. Default: (0.1, 0.5)

p float

Probability of applying the transform. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: 3

Note

  • This transform only works with RGB images. It will raise a TypeError if applied to non-RGB images.
  • The color shift is applied in the HSV color space, affecting the hue channel.
  • Luminance noise is added to all channels independently.
  • This transform can be useful for data augmentation in low-light scenarios or when training models to be robust against noisy inputs.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=0.5)\n>>> result = transform(image=image)\n>>> noisy_image = result[\"image\"]\n

References

  • ISO noise in digital photography: https://en.wikipedia.org/wiki/Image_noise#In_digital_cameras

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ISONoise(ImageOnlyTransform):\n    \"\"\"Applies camera sensor noise to the input image, simulating high ISO settings.\n\n    This transform adds random noise to an image, mimicking the effect of using high ISO settings\n    in digital photography. It simulates two main components of ISO noise:\n    1. Color noise: random shifts in color hue\n    2. Luminance noise: random variations in pixel intensity\n\n    Args:\n        color_shift (tuple[float, float]): Range for changing color hue.\n            Values should be in the range [0, 1], where 1 represents a full 360\u00b0 hue rotation.\n            Default: (0.01, 0.05)\n\n        intensity (tuple[float, float]): Range for the noise intensity.\n            Higher values increase the strength of both color and luminance noise.\n            Default: (0.1, 0.5)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - This transform only works with RGB images. It will raise a TypeError if applied to\n          non-RGB images.\n        - The color shift is applied in the HSV color space, affecting the hue channel.\n        - Luminance noise is added to all channels independently.\n        - This transform can be useful for data augmentation in low-light scenarios or when\n          training models to be robust against noisy inputs.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ISONoise(color_shift=(0.01, 0.05), intensity=(0.1, 0.5), p=0.5)\n        >>> result = transform(image=image)\n        >>> noisy_image = result[\"image\"]\n\n    References:\n        - ISO noise in digital photography:\n          https://en.wikipedia.org/wiki/Image_noise#In_digital_cameras\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        color_shift: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        intensity: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n\n    def __init__(\n        self,\n        color_shift: tuple[float, float] = (0.01, 0.05),\n        intensity: tuple[float, float] = (0.1, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.intensity = intensity\n        self.color_shift = color_shift\n\n    def apply(\n        self,\n        img: np.ndarray,\n        color_shift: float,\n        intensity: float,\n        random_seed: int,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.iso_noise(\n            img,\n            color_shift,\n            intensity,\n            np.random.default_rng(random_seed),\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        random_seed = self.random_generator.integers(0, 2**32 - 1)\n        return {\n            \"color_shift\": self.py_random.uniform(*self.color_shift),\n            \"intensity\": self.py_random.uniform(*self.intensity),\n            \"random_seed\": random_seed,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"intensity\", \"color_shift\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Illumination","title":"class Illumination (mode='linear', intensity_range=(0.01, 0.2), effect_type='both', angle_range=(0, 360), center_range=(0.1, 0.9), sigma_range=(0.2, 1.0), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply various illumination effects to the image.

This transform simulates different lighting conditions by applying controlled illumination patterns. It can create effects like: - Directional lighting (linear mode) - Corner shadows/highlights (corner mode) - Spotlights or local lighting (gaussian mode)

These effects can be used to: - Simulate natural lighting variations - Add dramatic lighting effects - Create synthetic shadows or highlights - Augment training data with different lighting conditions

Parameters:

Name Type Description mode Literal[\"linear\", \"corner\", \"gaussian\"]

Type of illumination pattern: - 'linear': Creates a smooth gradient across the image, simulating directional lighting like sunlight through a window - 'corner': Applies gradient from any corner, simulating light source from a corner - 'gaussian': Creates a circular spotlight effect, simulating local light sources Default: 'linear'

intensity_range tuple[float, float]

Range for effect strength. Values between 0.01 and 0.2: - 0.01-0.05: Subtle lighting changes - 0.05-0.1: Moderate lighting effects - 0.1-0.2: Strong lighting effects Default: (0.01, 0.2)

effect_type str

Type of lighting change: - 'brighten': Only adds light (like a spotlight) - 'darken': Only removes light (like a shadow) - 'both': Randomly chooses between brightening and darkening Default: 'both'

angle_range tuple[float, float]

Range for gradient angle in degrees. Controls direction of linear gradient: - 0\u00b0: Left to right - 90\u00b0: Top to bottom - 180\u00b0: Right to left - 270\u00b0: Bottom to top Only used for 'linear' mode. Default: (0, 360)

center_range tuple[float, float]

Range for spotlight position. Values between 0 and 1 representing relative position: - (0, 0): Top-left corner - (1, 1): Bottom-right corner - (0.5, 0.5): Center of image Only used for 'gaussian' mode. Default: (0.1, 0.9)

sigma_range tuple[float, float]

Range for spotlight size. Values between 0.2 and 1.0: - 0.2: Small, focused spotlight - 0.5: Medium-sized light area - 1.0: Broad, soft lighting Only used for 'gaussian' mode. Default: (0.2, 1.0)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import albumentations as A\n>>> # Simulate sunlight through window\n>>> transform = A.Illumination(\n...     mode='linear',\n...     intensity_range=(0.05, 0.1),\n...     effect_type='brighten',\n...     angle_range=(30, 60)\n... )\n>>>\n>>> # Create dramatic corner shadow\n>>> transform = A.Illumination(\n...     mode='corner',\n...     intensity_range=(0.1, 0.2),\n...     effect_type='darken'\n... )\n>>>\n>>> # Add multiple spotlights\n>>> transform1 = A.Illumination(\n...     mode='gaussian',\n...     intensity_range=(0.05, 0.15),\n...     effect_type='brighten',\n...     center_range=(0.2, 0.4),\n...     sigma_range=(0.2, 0.3)\n... )\n>>> transform2 = A.Illumination(\n...     mode='gaussian',\n...     intensity_range=(0.05, 0.15),\n...     effect_type='darken',\n...     center_range=(0.6, 0.8),\n...     sigma_range=(0.3, 0.5)\n... )\n>>> transforms = A.Compose([transform1, transform2])\n

References

  • Lighting in Computer Vision: https://en.wikipedia.org/wiki/Lighting_in_computer_vision

  • Image-based lighting: https://en.wikipedia.org/wiki/Image-based_lighting

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#randomlinearillumination

  • Research on lighting augmentation: \"Learning Deep Representations of Fine-grained Visual Descriptions\" https://arxiv.org/abs/1605.05395

  • Photography lighting patterns: https://en.wikipedia.org/wiki/Lighting_pattern

Note

  • The transform preserves image range and dtype
  • Effects are applied multiplicatively to preserve texture
  • Can be combined with other transforms for complex lighting scenarios
  • Useful for training models to be robust to lighting variations

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Illumination(ImageOnlyTransform):\n    \"\"\"Apply various illumination effects to the image.\n\n    This transform simulates different lighting conditions by applying controlled\n    illumination patterns. It can create effects like:\n    - Directional lighting (linear mode)\n    - Corner shadows/highlights (corner mode)\n    - Spotlights or local lighting (gaussian mode)\n\n    These effects can be used to:\n    - Simulate natural lighting variations\n    - Add dramatic lighting effects\n    - Create synthetic shadows or highlights\n    - Augment training data with different lighting conditions\n\n    Args:\n        mode (Literal[\"linear\", \"corner\", \"gaussian\"]): Type of illumination pattern:\n            - 'linear': Creates a smooth gradient across the image,\n                       simulating directional lighting like sunlight\n                       through a window\n            - 'corner': Applies gradient from any corner,\n                       simulating light source from a corner\n            - 'gaussian': Creates a circular spotlight effect,\n                         simulating local light sources\n            Default: 'linear'\n\n        intensity_range (tuple[float, float]): Range for effect strength.\n            Values between 0.01 and 0.2:\n            - 0.01-0.05: Subtle lighting changes\n            - 0.05-0.1: Moderate lighting effects\n            - 0.1-0.2: Strong lighting effects\n            Default: (0.01, 0.2)\n\n        effect_type (str): Type of lighting change:\n            - 'brighten': Only adds light (like a spotlight)\n            - 'darken': Only removes light (like a shadow)\n            - 'both': Randomly chooses between brightening and darkening\n            Default: 'both'\n\n        angle_range (tuple[float, float]): Range for gradient angle in degrees.\n            Controls direction of linear gradient:\n            - 0\u00b0: Left to right\n            - 90\u00b0: Top to bottom\n            - 180\u00b0: Right to left\n            - 270\u00b0: Bottom to top\n            Only used for 'linear' mode.\n            Default: (0, 360)\n\n        center_range (tuple[float, float]): Range for spotlight position.\n            Values between 0 and 1 representing relative position:\n            - (0, 0): Top-left corner\n            - (1, 1): Bottom-right corner\n            - (0.5, 0.5): Center of image\n            Only used for 'gaussian' mode.\n            Default: (0.1, 0.9)\n\n        sigma_range (tuple[float, float]): Range for spotlight size.\n            Values between 0.2 and 1.0:\n            - 0.2: Small, focused spotlight\n            - 0.5: Medium-sized light area\n            - 1.0: Broad, soft lighting\n            Only used for 'gaussian' mode.\n            Default: (0.2, 1.0)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Examples:\n        >>> import albumentations as A\n        >>> # Simulate sunlight through window\n        >>> transform = A.Illumination(\n        ...     mode='linear',\n        ...     intensity_range=(0.05, 0.1),\n        ...     effect_type='brighten',\n        ...     angle_range=(30, 60)\n        ... )\n        >>>\n        >>> # Create dramatic corner shadow\n        >>> transform = A.Illumination(\n        ...     mode='corner',\n        ...     intensity_range=(0.1, 0.2),\n        ...     effect_type='darken'\n        ... )\n        >>>\n        >>> # Add multiple spotlights\n        >>> transform1 = A.Illumination(\n        ...     mode='gaussian',\n        ...     intensity_range=(0.05, 0.15),\n        ...     effect_type='brighten',\n        ...     center_range=(0.2, 0.4),\n        ...     sigma_range=(0.2, 0.3)\n        ... )\n        >>> transform2 = A.Illumination(\n        ...     mode='gaussian',\n        ...     intensity_range=(0.05, 0.15),\n        ...     effect_type='darken',\n        ...     center_range=(0.6, 0.8),\n        ...     sigma_range=(0.3, 0.5)\n        ... )\n        >>> transforms = A.Compose([transform1, transform2])\n\n    References:\n        - Lighting in Computer Vision:\n          https://en.wikipedia.org/wiki/Lighting_in_computer_vision\n\n        - Image-based lighting:\n          https://en.wikipedia.org/wiki/Image-based_lighting\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#randomlinearillumination\n\n        - Research on lighting augmentation:\n          \"Learning Deep Representations of Fine-grained Visual Descriptions\"\n          https://arxiv.org/abs/1605.05395\n\n        - Photography lighting patterns:\n          https://en.wikipedia.org/wiki/Lighting_pattern\n\n    Note:\n        - The transform preserves image range and dtype\n        - Effects are applied multiplicatively to preserve texture\n        - Can be combined with other transforms for complex lighting scenarios\n        - Useful for training models to be robust to lighting variations\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: Literal[\"linear\", \"corner\", \"gaussian\"]\n        intensity_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0.01, 0.2)),\n        ]\n        effect_type: Literal[\"brighten\", \"darken\", \"both\"]\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 360)),\n        ]\n        center_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        sigma_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0.2, 1.0)),\n        ]\n\n    def __init__(\n        self,\n        mode: Literal[\"linear\", \"corner\", \"gaussian\"] = \"linear\",\n        intensity_range: tuple[float, float] = (0.01, 0.2),\n        effect_type: Literal[\"brighten\", \"darken\", \"both\"] = \"both\",\n        angle_range: tuple[float, float] = (0, 360),\n        center_range: tuple[float, float] = (0.1, 0.9),\n        sigma_range: tuple[float, float] = (0.2, 1.0),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(always_apply=always_apply, p=p)\n        self.mode = mode\n        self.intensity_range = intensity_range\n        self.effect_type = effect_type\n        self.angle_range = angle_range\n        self.center_range = center_range\n        self.sigma_range = sigma_range\n\n    def get_params(self) -> dict[str, Any]:\n        intensity = self.py_random.uniform(*self.intensity_range)\n\n        # Determine if brightening or darkening\n        sign = 1  # brighten\n        if self.effect_type == \"both\":\n            sign = 1 if self.py_random.random() > 0.5 else -1\n        elif self.effect_type == \"darken\":\n            sign = -1\n\n        intensity *= sign\n\n        if self.mode == \"linear\":\n            angle = self.py_random.uniform(*self.angle_range)\n            return {\n                \"intensity\": intensity,\n                \"angle\": angle,\n            }\n        if self.mode == \"corner\":\n            corner = self.py_random.randint(0, 3)  # Choose random corner\n            return {\n                \"intensity\": intensity,\n                \"corner\": corner,\n            }\n\n        x = self.py_random.uniform(*self.center_range)\n        y = self.py_random.uniform(*self.center_range)\n        sigma = self.py_random.uniform(*self.sigma_range)\n        return {\n            \"intensity\": intensity,\n            \"center\": (x, y),\n            \"sigma\": sigma,\n        }\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if self.mode == \"linear\":\n            return fmain.apply_linear_illumination(\n                img,\n                intensity=params[\"intensity\"],\n                angle=params[\"angle\"],\n            )\n        if self.mode == \"corner\":\n            return fmain.apply_corner_illumination(\n                img,\n                intensity=params[\"intensity\"],\n                corner=params[\"corner\"],\n            )\n\n        return fmain.apply_gaussian_illumination(\n            img,\n            intensity=params[\"intensity\"],\n            center=params[\"center\"],\n            sigma=params[\"sigma\"],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"mode\",\n            \"intensity_range\",\n            \"effect_type\",\n            \"angle_range\",\n            \"center_range\",\n            \"sigma_range\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ImageCompression","title":"class ImageCompression (quality_lower=None, quality_upper=None, compression_type='jpeg', quality_range=(99, 100), always_apply=None, p=0.5) [view source on GitHub]","text":"

Decrease image quality by applying JPEG or WebP compression.

This transform simulates the effect of saving an image with lower quality settings, which can introduce compression artifacts. It's useful for data augmentation and for testing model robustness against varying image qualities.

Parameters:

Name Type Description quality_range tuple[int, int]

Range for the compression quality. The values should be in [1, 100] range, where: - 1 is the lowest quality (maximum compression) - 100 is the highest quality (minimum compression) Default: (99, 100)

compression_type Literal[\"jpeg\", \"webp\"]

Type of compression to apply. - \"jpeg\": JPEG compression - \"webp\": WebP compression Default: \"jpeg\"

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform expects images with 1, 3, or 4 channels.
  • For JPEG compression, alpha channels (4th channel) will be ignored.
  • WebP compression supports transparency (4 channels).
  • The actual file is not saved to disk; the compression is simulated in memory.
  • Lower quality values result in smaller file sizes but may introduce visible artifacts.
  • This transform can be useful for:
  • Data augmentation to improve model robustness
  • Testing how models perform on images of varying quality
  • Simulating images transmitted over low-bandwidth connections

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ImageCompression(quality_range=(50, 90), compression_type=0, p=1.0)\n>>> result = transform(image=image)\n>>> compressed_image = result[\"image\"]\n

References

  • JPEG compression: https://en.wikipedia.org/wiki/JPEG
  • WebP compression: https://developers.google.com/speed/webp

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ImageCompression(ImageOnlyTransform):\n    \"\"\"Decrease image quality by applying JPEG or WebP compression.\n\n    This transform simulates the effect of saving an image with lower quality settings,\n    which can introduce compression artifacts. It's useful for data augmentation and\n    for testing model robustness against varying image qualities.\n\n    Args:\n        quality_range (tuple[int, int]): Range for the compression quality.\n            The values should be in [1, 100] range, where:\n            - 1 is the lowest quality (maximum compression)\n            - 100 is the highest quality (minimum compression)\n            Default: (99, 100)\n\n        compression_type (Literal[\"jpeg\", \"webp\"]): Type of compression to apply.\n            - \"jpeg\": JPEG compression\n            - \"webp\": WebP compression\n            Default: \"jpeg\"\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform expects images with 1, 3, or 4 channels.\n        - For JPEG compression, alpha channels (4th channel) will be ignored.\n        - WebP compression supports transparency (4 channels).\n        - The actual file is not saved to disk; the compression is simulated in memory.\n        - Lower quality values result in smaller file sizes but may introduce visible artifacts.\n        - This transform can be useful for:\n          * Data augmentation to improve model robustness\n          * Testing how models perform on images of varying quality\n          * Simulating images transmitted over low-bandwidth connections\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ImageCompression(quality_range=(50, 90), compression_type=0, p=1.0)\n        >>> result = transform(image=image)\n        >>> compressed_image = result[\"image\"]\n\n    References:\n        - JPEG compression: https://en.wikipedia.org/wiki/JPEG\n        - WebP compression: https://developers.google.com/speed/webp\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        quality_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, 100)),\n            AfterValidator(nondecreasing),\n        ]\n\n        quality_lower: int | None = Field(\n            ge=1,\n            le=100,\n        )\n        quality_upper: int | None = Field(\n            ge=1,\n            le=100,\n        )\n        compression_type: Literal[\"jpeg\", \"webp\"]\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            # Update the quality_range based on the non-None values of quality_lower and quality_upper\n            if self.quality_lower is not None or self.quality_upper is not None:\n                if self.quality_lower is not None:\n                    warn(\n                        \"`quality_lower` is deprecated. Use `quality_range` as tuple\"\n                        \" (quality_lower, quality_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.quality_upper is not None:\n                    warn(\n                        \"`quality_upper` is deprecated. Use `quality_range` as tuple\"\n                        \" (quality_lower, quality_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.quality_lower if self.quality_lower is not None else self.quality_range[0]\n                upper = self.quality_upper if self.quality_upper is not None else self.quality_range[1]\n                self.quality_range = (lower, upper)\n                # Clear the deprecated individual quality settings\n                self.quality_lower = None\n                self.quality_upper = None\n\n            # Validate the quality_range\n            if not (1 <= self.quality_range[0] <= MAX_JPEG_QUALITY and 1 <= self.quality_range[1] <= MAX_JPEG_QUALITY):\n                raise ValueError(\n                    f\"Quality range values should be within [1, {MAX_JPEG_QUALITY}] range.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        quality_lower: int | None = None,\n        quality_upper: int | None = None,\n        compression_type: Literal[\"jpeg\", \"webp\"] = \"jpeg\",\n        quality_range: tuple[int, int] = (99, 100),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.quality_range = quality_range\n        self.compression_type = compression_type\n\n    def apply(\n        self,\n        img: np.ndarray,\n        quality: int,\n        image_type: Literal[\".jpg\", \".webp\"],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.image_compression(img, quality, image_type)\n\n    def get_params(self) -> dict[str, int | str]:\n        if self.compression_type == \"jpeg\":\n            image_type = \".jpg\"\n        elif self.compression_type == \"webp\":\n            image_type = \".webp\"\n        else:\n            raise ValueError(f\"Unknown image compression type: {self.compression_type}\")\n\n        return {\n            \"quality\": self.py_random.randint(*self.quality_range),\n            \"image_type\": image_type,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"quality_range\", \"compression_type\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.InterpolationPydantic","title":"class InterpolationPydantic ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class InterpolationPydantic(BaseModel):\n    upscale: InterpolationType\n    downscale: InterpolationType\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.InvertImg","title":"class InvertImg [view source on GitHub]","text":"

Invert the input image by subtracting pixel values from max values of the image types, i.e., 255 for uint8 and 1.0 for float32.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class InvertImg(ImageOnlyTransform):\n    \"\"\"Invert the input image by subtracting pixel values from max values of the image types,\n    i.e., 255 for uint8 and 1.0 for float32.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    \"\"\"\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.invert(img)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Lambda","title":"class Lambda (image=None, mask=None, keypoints=None, bboxes=None, name=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

A flexible transformation class for using user-defined transformation functions per targets. Function signature must include **kwargs to accept optional arguments like interpolation method, image size, etc:

Parameters:

Name Type Description image Callable[..., Any] | None

Image transformation function.

mask Callable[..., Any] | None

Mask transformation function.

keypoints Callable[..., Any] | None

Keypoints transformation function.

bboxes Callable[..., Any] | None

BBoxes transformation function.

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Number of channels: Any

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Lambda(NoOp):\n    \"\"\"A flexible transformation class for using user-defined transformation functions per targets.\n    Function signature must include **kwargs to accept optional arguments like interpolation method, image size, etc:\n\n    Args:\n        image: Image transformation function.\n        mask: Mask transformation function.\n        keypoints: Keypoints transformation function.\n        bboxes: BBoxes transformation function.\n        p: probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    \"\"\"\n\n    def __init__(\n        self,\n        image: Callable[..., Any] | None = None,\n        mask: Callable[..., Any] | None = None,\n        keypoints: Callable[..., Any] | None = None,\n        bboxes: Callable[..., Any] | None = None,\n        name: str | None = None,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.name = name\n        self.custom_apply_fns = {\n            target_name: fmain.noop for target_name in (\"image\", \"mask\", \"keypoints\", \"bboxes\", \"global_label\")\n        }\n        for target_name, custom_apply_fn in {\n            \"image\": image,\n            \"mask\": mask,\n            \"keypoints\": keypoints,\n            \"bboxes\": bboxes,\n        }.items():\n            if custom_apply_fn is not None:\n                if isinstance(custom_apply_fn, LambdaType) and custom_apply_fn.__name__ == \"<lambda>\":\n                    warnings.warn(\n                        \"Using lambda is incompatible with multiprocessing. \"\n                        \"Consider using regular functions or partial().\",\n                        stacklevel=2,\n                    )\n\n                self.custom_apply_fns[target_name] = custom_apply_fn\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        fn = self.custom_apply_fns[\"image\"]\n        return fn(img, **params)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        fn = self.custom_apply_fns[\"mask\"]\n        return fn(mask, **params)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        is_ndarray = True\n\n        if not isinstance(bboxes, np.ndarray):\n            is_ndarray = False\n            bboxes = np.array(bboxes, dtype=np.float32)\n\n        fn = self.custom_apply_fns[\"bboxes\"]\n        result = fn(bboxes, **params)\n\n        if not is_ndarray:\n            return result.tolist()\n\n        return result\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        is_ndarray = True\n        if not isinstance(keypoints, np.ndarray):\n            is_ndarray = False\n            keypoints = np.array(keypoints, dtype=np.float32)\n\n        fn = self.custom_apply_fns[\"keypoints\"]\n        result = fn(keypoints, **params)\n\n        if not is_ndarray:\n            return result.tolist()\n\n        return result\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a Lambda transform serializable you should provide the `name` argument, \"\n                \"e.g. `Lambda(name='my_transform', image=<some func>, ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n\n    def __repr__(self) -> str:\n        state = {\"name\": self.name}\n        state.update(self.custom_apply_fns.items())  # type: ignore[arg-type]\n        state.update(self.get_base_init_args())\n        return f\"{self.__class__.__name__}({format_args(state)})\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.LaplaceParams","title":"class LaplaceParams ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class LaplaceParams(NoiseParamsBase):\n    noise_type: Literal[\"laplace\"] = \"laplace\"\n    mean_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=-1, max_val=1)),\n    ]\n    scale_range: Annotated[\n        Sequence[float],\n        AfterValidator(check_range_bounds(min_val=0, max_val=1)),\n    ]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Morphological","title":"class Morphological (scale=(2, 3), operation='dilation', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply a morphological operation (dilation or erosion) to an image, with particular value for enhancing document scans.

Morphological operations modify the structure of the image. Dilation expands the white (foreground) regions in a binary or grayscale image, while erosion shrinks them. These operations are beneficial in document processing, for example: - Dilation helps in closing up gaps within text or making thin lines thicker, enhancing legibility for OCR (Optical Character Recognition). - Erosion can remove small white noise and detach connected objects, making the structure of larger objects more pronounced.

Parameters:

Name Type Description scale int or tuple/list of int

Specifies the size of the structuring element (kernel) used for the operation. - If an integer is provided, a square kernel of that size will be used. - If a tuple or list is provided, it should contain two integers representing the minimum and maximum sizes for the dilation kernel.

operation Literal[\"erosion\", \"dilation\"]

The morphological operation to apply. Default is 'dilation'.

p float

The probability of applying this transformation. Default is 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

https://github.com/facebookresearch/nougat

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n>>>     A.Morphological(scale=(2, 3), operation='dilation', p=0.5)\n>>> ])\n>>> image = transform(image=image)[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Morphological(DualTransform):\n    \"\"\"Apply a morphological operation (dilation or erosion) to an image,\n    with particular value for enhancing document scans.\n\n    Morphological operations modify the structure of the image.\n    Dilation expands the white (foreground) regions in a binary or grayscale image, while erosion shrinks them.\n    These operations are beneficial in document processing, for example:\n    - Dilation helps in closing up gaps within text or making thin lines thicker,\n        enhancing legibility for OCR (Optical Character Recognition).\n    - Erosion can remove small white noise and detach connected objects,\n        making the structure of larger objects more pronounced.\n\n    Args:\n        scale (int or tuple/list of int): Specifies the size of the structuring element (kernel) used for the operation.\n            - If an integer is provided, a square kernel of that size will be used.\n            - If a tuple or list is provided, it should contain two integers representing the minimum\n                and maximum sizes for the dilation kernel.\n        operation (Literal[\"erosion\", \"dilation\"]): The morphological operation to apply.\n            Default is 'dilation'.\n        p (float, optional): The probability of applying this transformation. Default is 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://github.com/facebookresearch/nougat\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        >>>     A.Morphological(scale=(2, 3), operation='dilation', p=0.5)\n        >>> ])\n        >>> image = transform(image=image)[\"image\"]\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: OnePlusIntRangeType\n        operation: MorphologyMode\n\n    def __init__(\n        self,\n        scale: ScaleIntType = (2, 3),\n        operation: MorphologyMode = \"dilation\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale = cast(tuple[int, int], scale)\n        self.operation = operation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.morphology(img, kernel, self.operation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"]\n\n        denormalized_boxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fmain.bboxes_morphology(\n            denormalized_boxes,\n            kernel,\n            self.operation,\n            image_shape,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        kernel: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return keypoints\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"kernel\": cv2.getStructuringElement(cv2.MORPH_ELLIPSE, self.scale),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"scale\", \"operation\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.MultiplicativeNoise","title":"class MultiplicativeNoise (multiplier=(0.9, 1.1), per_channel=False, elementwise=False, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply multiplicative noise to the input image.

This transform multiplies each pixel in the image by a random value or array of values, effectively creating a noise pattern that scales with the image intensity.

Parameters:

Name Type Description multiplier tuple[float, float]

The range for the random multiplier. Defines the range from which the multiplier is sampled. Default: (0.9, 1.1)

per_channel bool

If True, use a different random multiplier for each channel. If False, use the same multiplier for all channels. Setting this to False is slightly faster. Default: False

elementwise bool

If True, generates a unique multiplier for each pixel. If False, generates a single multiplier (or one per channel if per_channel=True). Default: False

p float

Probability of applying the transform. Default: 0.5

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • When elementwise=False and per_channel=False, a single multiplier is applied to the entire image.
  • When elementwise=False and per_channel=True, each channel gets a different multiplier.
  • When elementwise=True and per_channel=False, each pixel gets the same multiplier across all channels.
  • When elementwise=True and per_channel=True, each pixel in each channel gets a unique multiplier.
  • Setting per_channel=False is slightly faster, especially for larger images.
  • This transform can be used to simulate various lighting conditions or to create noise that scales with image intensity.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.MultiplicativeNoise(multiplier=(0.9, 1.1), per_channel=True, p=1.0)\n>>> result = transform(image=image)\n>>> noisy_image = result[\"image\"]\n

References

  • Multiplicative noise: https://en.wikipedia.org/wiki/Multiplicative_noise

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class MultiplicativeNoise(ImageOnlyTransform):\n    \"\"\"Apply multiplicative noise to the input image.\n\n    This transform multiplies each pixel in the image by a random value or array of values,\n    effectively creating a noise pattern that scales with the image intensity.\n\n    Args:\n        multiplier (tuple[float, float]): The range for the random multiplier.\n            Defines the range from which the multiplier is sampled.\n            Default: (0.9, 1.1)\n\n        per_channel (bool): If True, use a different random multiplier for each channel.\n            If False, use the same multiplier for all channels.\n            Setting this to False is slightly faster.\n            Default: False\n\n        elementwise (bool): If True, generates a unique multiplier for each pixel.\n            If False, generates a single multiplier (or one per channel if per_channel=True).\n            Default: False\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - When elementwise=False and per_channel=False, a single multiplier is applied to the entire image.\n        - When elementwise=False and per_channel=True, each channel gets a different multiplier.\n        - When elementwise=True and per_channel=False, each pixel gets the same multiplier across all channels.\n        - When elementwise=True and per_channel=True, each pixel in each channel gets a unique multiplier.\n        - Setting per_channel=False is slightly faster, especially for larger images.\n        - This transform can be used to simulate various lighting conditions or to create noise that\n          scales with image intensity.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.MultiplicativeNoise(multiplier=(0.9, 1.1), per_channel=True, p=1.0)\n        >>> result = transform(image=image)\n        >>> noisy_image = result[\"image\"]\n\n    References:\n        - Multiplicative noise: https://en.wikipedia.org/wiki/Multiplicative_noise\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        multiplier: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        per_channel: bool\n        elementwise: bool\n\n    def __init__(\n        self,\n        multiplier: ScaleFloatType = (0.9, 1.1),\n        per_channel: bool = False,\n        elementwise: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.multiplier = cast(tuple[float, float], multiplier)\n        self.elementwise = elementwise\n        self.per_channel = per_channel\n\n    def apply(\n        self,\n        img: np.ndarray,\n        multiplier: float | np.ndarray,\n        **kwargs: Any,\n    ) -> np.ndarray:\n        return multiply(img, multiplier)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if self.elementwise:\n            shape = image.shape if self.per_channel else (*image.shape[:2], 1)\n        else:\n            shape = (num_channels,) if self.per_channel else (1,)\n\n        multiplier = self.random_generator.uniform(\n            self.multiplier[0],\n            self.multiplier[1],\n            shape,\n        ).astype(np.float32)\n\n        if not self.per_channel and num_channels > 1:\n            # Replicate the multiplier for all channels if not per_channel\n            multiplier = np.repeat(multiplier, num_channels, axis=-1)\n\n        if not self.elementwise and self.per_channel:\n            # Reshape to broadcast correctly when not elementwise but per_channel\n            multiplier = multiplier.reshape(1, 1, -1)\n\n        if multiplier.shape != image.shape:\n            multiplier = multiplier.squeeze()\n\n        return {\"multiplier\": multiplier}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"multiplier\", \"elementwise\", \"per_channel\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.NoiseParamsBase","title":"class NoiseParamsBase ","text":"

Base class for all noise parameter models.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class NoiseParamsBase(BaseModel):\n    \"\"\"Base class for all noise parameter models.\"\"\"\n\n    model_config = ConfigDict(extra=\"forbid\")\n    noise_type: str\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Normalize","title":"class Normalize (mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), max_pixel_value=255.0, normalization='standard', always_apply=None, p=1.0) [view source on GitHub]","text":"

Applies various normalization techniques to an image. The specific normalization technique can be selected with the normalization parameter.

Standard normalization is applied using the formula: img = (img - mean * max_pixel_value) / (std * max_pixel_value). Other normalization techniques adjust the image based on global or per-channel statistics, or scale pixel values to a specified range.

Parameters:

Name Type Description mean ColorType | None

Mean values for standard normalization. For \"standard\" normalization, the default values are ImageNet mean values: (0.485, 0.456, 0.406).

std ColorType | None

Standard deviation values for standard normalization. For \"standard\" normalization, the default values are ImageNet standard deviation :(0.229, 0.224, 0.225).

max_pixel_value float | None

Maximum possible pixel value, used for scaling in standard normalization. Defaults to 255.0.

normalization Literal[\"standard\", \"image\", \"image_per_channel\", \"min_max\", \"min_max_per_channel\"]) Specifies the normalization technique to apply. Defaults to \"standard\". - \"standard\"

Applies the formula (img - mean * max_pixel_value) / (std * max_pixel_value). The default mean and std are based on ImageNet. You can use mean and std values of (0.5, 0.5, 0.5) for inception normalization. And mean values of (0, 0, 0) and std values of (1, 1, 1) for YOLO. - \"image\": Normalizes the whole image based on its global mean and standard deviation. - \"image_per_channel\": Normalizes the image per channel based on each channel's mean and standard deviation. - \"min_max\": Scales the image pixel values to a [0, 1] range based on the global minimum and maximum pixel values. - \"min_max_per_channel\": Scales each channel of the image pixel values to a [0, 1] range based on the per-channel minimum and maximum pixel values.

p float

Probability of applying the transform. Defaults to 1.0.

Targets

image

Image types: uint8, float32

Note

  • For \"standard\" normalization, mean, std, and max_pixel_value must be provided.
  • For other normalization types, these parameters are ignored.
  • For inception normalization, use mean values of (0.5, 0.5, 0.5).
  • For YOLO normalization, use mean values of (0, 0, 0) and std values of (1, 1, 1).
  • This transform is often used as a final step in image preprocessing pipelines to prepare images for neural network input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Standard ImageNet normalization\n>>> transform = A.Normalize(\n...     mean=(0.485, 0.456, 0.406),\n...     std=(0.229, 0.224, 0.225),\n...     max_pixel_value=255.0,\n...     p=1.0\n... )\n>>> normalized_image = transform(image=image)[\"image\"]\n>>>\n>>> # Min-max normalization\n>>> transform_minmax = A.Normalize(normalization=\"min_max\", p=1.0)\n>>> normalized_image_minmax = transform_minmax(image=image)[\"image\"]\n

References

  • ImageNet mean and std: https://pytorch.org/vision/stable/models.html
  • Inception preprocessing: https://keras.io/api/applications/inceptionv3/

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Normalize(ImageOnlyTransform):\n    \"\"\"Applies various normalization techniques to an image. The specific normalization technique can be selected\n        with the `normalization` parameter.\n\n    Standard normalization is applied using the formula:\n        `img = (img - mean * max_pixel_value) / (std * max_pixel_value)`.\n        Other normalization techniques adjust the image based on global or per-channel statistics,\n        or scale pixel values to a specified range.\n\n    Args:\n        mean (ColorType | None): Mean values for standard normalization.\n            For \"standard\" normalization, the default values are ImageNet mean values: (0.485, 0.456, 0.406).\n        std (ColorType | None): Standard deviation values for standard normalization.\n            For \"standard\" normalization, the default values are ImageNet standard deviation :(0.229, 0.224, 0.225).\n        max_pixel_value (float | None): Maximum possible pixel value, used for scaling in standard normalization.\n            Defaults to 255.0.\n        normalization (Literal[\"standard\", \"image\", \"image_per_channel\", \"min_max\", \"min_max_per_channel\"])\n            Specifies the normalization technique to apply. Defaults to \"standard\".\n            - \"standard\": Applies the formula `(img - mean * max_pixel_value) / (std * max_pixel_value)`.\n                The default mean and std are based on ImageNet. You can use mean and std values of (0.5, 0.5, 0.5)\n                for inception normalization. And mean values of (0, 0, 0) and std values of (1, 1, 1) for YOLO.\n            - \"image\": Normalizes the whole image based on its global mean and standard deviation.\n            - \"image_per_channel\": Normalizes the image per channel based on each channel's mean and standard deviation.\n            - \"min_max\": Scales the image pixel values to a [0, 1] range based on the global\n                minimum and maximum pixel values.\n            - \"min_max_per_channel\": Scales each channel of the image pixel values to a [0, 1]\n                range based on the per-channel minimum and maximum pixel values.\n\n        p (float): Probability of applying the transform. Defaults to 1.0.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - For \"standard\" normalization, `mean`, `std`, and `max_pixel_value` must be provided.\n        - For other normalization types, these parameters are ignored.\n        - For inception normalization, use mean values of (0.5, 0.5, 0.5).\n        - For YOLO normalization, use mean values of (0, 0, 0) and std values of (1, 1, 1).\n        - This transform is often used as a final step in image preprocessing pipelines to\n          prepare images for neural network input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Standard ImageNet normalization\n        >>> transform = A.Normalize(\n        ...     mean=(0.485, 0.456, 0.406),\n        ...     std=(0.229, 0.224, 0.225),\n        ...     max_pixel_value=255.0,\n        ...     p=1.0\n        ... )\n        >>> normalized_image = transform(image=image)[\"image\"]\n        >>>\n        >>> # Min-max normalization\n        >>> transform_minmax = A.Normalize(normalization=\"min_max\", p=1.0)\n        >>> normalized_image_minmax = transform_minmax(image=image)[\"image\"]\n\n    References:\n        - ImageNet mean and std: https://pytorch.org/vision/stable/models.html\n        - Inception preprocessing: https://keras.io/api/applications/inceptionv3/\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mean: ColorType | None\n        std: ColorType | None\n        max_pixel_value: float | None\n        normalization: Literal[\n            \"standard\",\n            \"image\",\n            \"image_per_channel\",\n            \"min_max\",\n            \"min_max_per_channel\",\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_normalization(self) -> Self:\n            if (\n                self.mean is None\n                or self.std is None\n                or (self.max_pixel_value is None and self.normalization == \"standard\")\n            ):\n                raise ValueError(\n                    \"mean, std, and max_pixel_value must be provided for standard normalization.\",\n                )\n            return self\n\n    def __init__(\n        self,\n        mean: ColorType | None = (0.485, 0.456, 0.406),\n        std: ColorType | None = (0.229, 0.224, 0.225),\n        max_pixel_value: float | None = 255.0,\n        normalization: Literal[\n            \"standard\",\n            \"image\",\n            \"image_per_channel\",\n            \"min_max\",\n            \"min_max_per_channel\",\n        ] = \"standard\",\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.mean = mean\n        self.mean_np = np.array(mean, dtype=np.float32) * max_pixel_value\n        self.std = std\n        self.denominator = np.reciprocal(\n            np.array(std, dtype=np.float32) * max_pixel_value,\n        )\n        self.max_pixel_value = max_pixel_value\n        self.normalization = normalization\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if self.normalization == \"standard\":\n            return normalize(\n                img,\n                self.mean_np,\n                self.denominator,\n            )\n        return normalize_per_image(img, self.normalization)\n\n    @batch_transform(\"channel\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(images, **params)\n\n    @batch_transform(\"channel\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(volume, **params)\n\n    @batch_transform(\"channel\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, **params: Any) -> np.ndarray:\n        return self.apply(volumes, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mean\", \"std\", \"max_pixel_value\", \"normalization\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PixelDropout","title":"class PixelDropout (dropout_prob=0.01, per_channel=False, drop_value=0, mask_drop_value=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Drops random pixels from the image.

This transform randomly sets pixels in the image to a specified value, effectively \"dropping out\" those pixels. It can be applied to both the image and its corresponding mask.

Parameters:

Name Type Description dropout_prob float

Probability of dropping out each pixel. Should be in the range [0, 1]. Default: 0.01

per_channel bool

If True, the dropout mask will be generated independently for each channel. If False, the same dropout mask will be applied to all channels. Default: False

drop_value float | Sequence[float] | None

Value to assign to the dropped pixels. If None, the value will be randomly sampled for each application: - For uint8 images: Random integer in [0, 255] - For float32 images: Random float in [0, 1] If a single number, that value will be used for all dropped pixels. If a sequence, it should contain one value per channel. Default: 0

mask_drop_value float | Sequence[float] | None

Value to assign to dropped pixels in the mask. If None, the mask will remain unchanged. If a single number, that value will be used for all dropped pixels in the mask. If a sequence, it should contain one value per channel of the mask. Note: Only applicable when per_channel=False. Default: None

always_apply bool

If True, the transform will always be applied. Default: False

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • When applied to bounding boxes, this transform may cause some boxes to have zero area if all pixels within the box are dropped. Such boxes will be removed.
  • When applied to keypoints, keypoints that fall on dropped pixels will be removed if the keypoint processor is configured to remove invisible keypoints.
  • The 'per_channel' option is not supported for mask dropout. If you need to drop pixels in a multi-channel mask independently, consider applying this transform multiple times with per_channel=False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> transform = A.PixelDropout(dropout_prob=0.1, per_channel=True, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> dropped_image, dropped_mask = result['image'], result['mask']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PixelDropout(DualTransform):\n    \"\"\"Drops random pixels from the image.\n\n    This transform randomly sets pixels in the image to a specified value, effectively \"dropping out\" those pixels.\n    It can be applied to both the image and its corresponding mask.\n\n    Args:\n        dropout_prob (float): Probability of dropping out each pixel. Should be in the range [0, 1].\n            Default: 0.01\n\n        per_channel (bool): If True, the dropout mask will be generated independently for each channel.\n            If False, the same dropout mask will be applied to all channels.\n            Default: False\n\n        drop_value (float | Sequence[float] | None): Value to assign to the dropped pixels.\n            If None, the value will be randomly sampled for each application:\n                - For uint8 images: Random integer in [0, 255]\n                - For float32 images: Random float in [0, 1]\n            If a single number, that value will be used for all dropped pixels.\n            If a sequence, it should contain one value per channel.\n            Default: 0\n\n        mask_drop_value (float | Sequence[float] | None): Value to assign to dropped pixels in the mask.\n            If None, the mask will remain unchanged.\n            If a single number, that value will be used for all dropped pixels in the mask.\n            If a sequence, it should contain one value per channel of the mask.\n            Note: Only applicable when per_channel=False.\n            Default: None\n\n        always_apply (bool): If True, the transform will always be applied.\n            Default: False\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - When applied to bounding boxes, this transform may cause some boxes to have zero area\n          if all pixels within the box are dropped. Such boxes will be removed.\n        - When applied to keypoints, keypoints that fall on dropped pixels will be removed if\n          the keypoint processor is configured to remove invisible keypoints.\n        - The 'per_channel' option is not supported for mask dropout. If you need to drop pixels\n          in a multi-channel mask independently, consider applying this transform multiple times\n          with per_channel=False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> transform = A.PixelDropout(dropout_prob=0.1, per_channel=True, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> dropped_image, dropped_mask = result['image'], result['mask']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        dropout_prob: ProbabilityType\n        per_channel: bool\n        drop_value: ScaleFloatType | None\n        mask_drop_value: ScaleFloatType | None\n\n        @model_validator(mode=\"after\")\n        def validate_mask_drop_value(self) -> Self:\n            if self.mask_drop_value is not None and self.per_channel:\n                msg = \"PixelDropout supports mask only with per_channel=False.\"\n                raise ValueError(msg)\n            return self\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        dropout_prob: float = 0.01,\n        per_channel: bool = False,\n        drop_value: ScaleFloatType | None = 0,\n        mask_drop_value: ScaleFloatType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.dropout_prob = dropout_prob\n        self.per_channel = per_channel\n        self.drop_value = drop_value\n        self.mask_drop_value = mask_drop_value\n\n    def apply(\n        self,\n        img: np.ndarray,\n        drop_mask: np.ndarray,\n        drop_value: float | Sequence[float],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.pixel_dropout(img, drop_mask, drop_value)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        drop_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        if self.mask_drop_value is None:\n            return mask\n\n        if mask.ndim == MONO_CHANNEL_DIMENSIONS:\n            drop_mask = np.squeeze(drop_mask)\n\n        return fmain.pixel_dropout(mask, drop_mask, self.mask_drop_value)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        drop_mask: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if drop_mask is None or self.per_channel:\n            return bboxes\n\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n\n        image_shape = params[\"shape\"][:2]\n\n        denormalized_bboxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fdropout.mask_dropout_bboxes(\n            denormalized_bboxes,\n            drop_mask,\n            image_shape,\n            processor.params.min_area,\n            processor.params.min_visibility,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        drop_mask: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if drop_mask is None or self.per_channel:\n            return keypoints\n\n        processor = cast(KeypointsProcessor, self.get_processor(\"keypoints\"))\n\n        if processor is None or not processor.params.remove_invisible:\n            return keypoints\n\n        return fdropout.mask_dropout_keypoints(keypoints, drop_mask)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        shape = image.shape if self.per_channel else image.shape[:2]\n\n        # Use choice to create boolean matrix, if we will use binomial after that we will need type conversion\n        drop_mask = self.random_generator.choice(\n            [True, False],\n            shape,\n            p=[self.dropout_prob, 1 - self.dropout_prob],\n        )\n\n        drop_value: float | Sequence[float] | np.ndarray\n\n        if drop_mask.ndim != image.ndim:\n            drop_mask = np.expand_dims(drop_mask, -1)\n        if self.drop_value is None:\n            drop_shape = 1 if is_grayscale_image(image) else int(image.shape[-1])\n\n            if image.dtype == np.uint8:\n                drop_value = self.random_generator.integers(\n                    0,\n                    int(MAX_VALUES_BY_DTYPE[image.dtype]),\n                    size=drop_shape,\n                    dtype=image.dtype,\n                )\n            elif image.dtype == np.float32:\n                drop_value = self.random_generator.uniform(\n                    0,\n                    1,\n                    size=drop_shape,\n                ).astype(image.dtype)\n            else:\n                raise ValueError(f\"Unsupported dtype: {image.dtype}\")\n        else:\n            drop_value = self.drop_value\n\n        return {\"drop_mask\": drop_mask, \"drop_value\": drop_value}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return (\"dropout_prob\", \"per_channel\", \"drop_value\", \"mask_drop_value\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlanckianJitter","title":"class PlanckianJitter (mode='blackbody', temperature_limit=None, sampling_method='uniform', p=0.5, always_apply=None) [view source on GitHub]","text":"

Applies Planckian Jitter to the input image, simulating color temperature variations in illumination.

This transform adjusts the color of an image to mimic the effect of different color temperatures of light sources, based on Planck's law of black body radiation. It can simulate the appearance of an image under various lighting conditions, from warm (reddish) to cool (bluish) color casts.

PlanckianJitter vs. ColorJitter: PlanckianJitter is fundamentally different from ColorJitter in its approach and use cases: 1. Physics-based: PlanckianJitter is grounded in the physics of light, simulating real-world color temperature changes. ColorJitter applies arbitrary color adjustments. 2. Natural effects: This transform produces color shifts that correspond to natural lighting variations, making it ideal for outdoor scene simulation or color constancy problems. 3. Single parameter: Color changes are controlled by a single, physically meaningful parameter (color temperature), unlike ColorJitter's multiple abstract parameters. 4. Correlated changes: Color shifts are correlated across channels in a way that mimics natural light, whereas ColorJitter can make independent channel adjustments.

When to use PlanckianJitter: - Simulating different times of day or lighting conditions in outdoor scenes - Augmenting data for computer vision tasks that need to be robust to natural lighting changes - Preparing synthetic data to better match real-world lighting variations - Color constancy research or applications - When you need physically plausible color variations rather than arbitrary color changes

The logic behind PlanckianJitter: As the color temperature increases: 1. Lower temperatures (around 3000K) produce warm, reddish tones, simulating sunset or incandescent lighting. 2. Mid-range temperatures (around 5500K) correspond to daylight. 3. Higher temperatures (above 7000K) result in cool, bluish tones, similar to overcast sky or shade. This progression mimics the natural variation of sunlight throughout the day and in different weather conditions.

Parameters:

Name Type Description mode Literal[\"blackbody\", \"cied\"]

The mode of the transformation. - \"blackbody\": Simulates blackbody radiation color changes. - \"cied\": Uses the CIE D illuminant series for color temperature simulation. Default: \"blackbody\"

temperature_limit tuple[int, int] | None

The range of color temperatures (in Kelvin) to sample from. - For \"blackbody\" mode: Should be within [3000K, 15000K]. Default: (3000, 15000) - For \"cied\" mode: Should be within [4000K, 15000K]. Default: (4000, 15000) If None, the default ranges will be used based on the selected mode. Higher temperatures produce cooler (bluish) images, lower temperatures produce warmer (reddish) images.

sampling_method Literal[\"uniform\", \"gaussian\"]

Method to sample the temperature. - \"uniform\": Samples uniformly across the specified range. - \"gaussian\": Samples from a Gaussian distribution centered at 6500K (approximate daylight). Default: \"uniform\"

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The transform preserves the overall brightness of the image while shifting its color.
  • The \"blackbody\" mode provides a wider range of color shifts, especially in the lower (warmer) temperatures.
  • The \"cied\" mode is based on standard illuminants and may provide more realistic daylight variations.
  • The Gaussian sampling method tends to produce more subtle variations, as it's centered around daylight.
  • Unlike ColorJitter, this transform ensures that color changes are physically plausible and correlated across channels, maintaining the natural appearance of the scene under different lighting conditions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PlanckianJitter(mode=\"blackbody\",\n...                               temperature_range=(3000, 9000),\n...                               sampling_method=\"uniform\",\n...                               p=1.0)\n>>> result = transform(image=image)\n>>> jittered_image = result[\"image\"]\n

References

  • Planck's law: https://en.wikipedia.org/wiki/Planck%27s_law
  • CIE Standard Illuminants: https://en.wikipedia.org/wiki/Standard_illuminant
  • Color temperature: https://en.wikipedia.org/wiki/Color_temperature
  • Implementation inspired by: https://github.com/TheZino/PlanckianJitter

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlanckianJitter(ImageOnlyTransform):\n    \"\"\"Applies Planckian Jitter to the input image, simulating color temperature variations in illumination.\n\n    This transform adjusts the color of an image to mimic the effect of different color temperatures\n    of light sources, based on Planck's law of black body radiation. It can simulate the appearance\n    of an image under various lighting conditions, from warm (reddish) to cool (bluish) color casts.\n\n    PlanckianJitter vs. ColorJitter:\n    PlanckianJitter is fundamentally different from ColorJitter in its approach and use cases:\n    1. Physics-based: PlanckianJitter is grounded in the physics of light, simulating real-world\n       color temperature changes. ColorJitter applies arbitrary color adjustments.\n    2. Natural effects: This transform produces color shifts that correspond to natural lighting\n       variations, making it ideal for outdoor scene simulation or color constancy problems.\n    3. Single parameter: Color changes are controlled by a single, physically meaningful parameter\n       (color temperature), unlike ColorJitter's multiple abstract parameters.\n    4. Correlated changes: Color shifts are correlated across channels in a way that mimics natural\n       light, whereas ColorJitter can make independent channel adjustments.\n\n    When to use PlanckianJitter:\n    - Simulating different times of day or lighting conditions in outdoor scenes\n    - Augmenting data for computer vision tasks that need to be robust to natural lighting changes\n    - Preparing synthetic data to better match real-world lighting variations\n    - Color constancy research or applications\n    - When you need physically plausible color variations rather than arbitrary color changes\n\n    The logic behind PlanckianJitter:\n    As the color temperature increases:\n    1. Lower temperatures (around 3000K) produce warm, reddish tones, simulating sunset or incandescent lighting.\n    2. Mid-range temperatures (around 5500K) correspond to daylight.\n    3. Higher temperatures (above 7000K) result in cool, bluish tones, similar to overcast sky or shade.\n    This progression mimics the natural variation of sunlight throughout the day and in different weather conditions.\n\n    Args:\n        mode (Literal[\"blackbody\", \"cied\"]): The mode of the transformation.\n            - \"blackbody\": Simulates blackbody radiation color changes.\n            - \"cied\": Uses the CIE D illuminant series for color temperature simulation.\n            Default: \"blackbody\"\n\n        temperature_limit (tuple[int, int] | None): The range of color temperatures (in Kelvin) to sample from.\n            - For \"blackbody\" mode: Should be within [3000K, 15000K]. Default: (3000, 15000)\n            - For \"cied\" mode: Should be within [4000K, 15000K]. Default: (4000, 15000)\n            If None, the default ranges will be used based on the selected mode.\n            Higher temperatures produce cooler (bluish) images, lower temperatures produce warmer (reddish) images.\n\n        sampling_method (Literal[\"uniform\", \"gaussian\"]): Method to sample the temperature.\n            - \"uniform\": Samples uniformly across the specified range.\n            - \"gaussian\": Samples from a Gaussian distribution centered at 6500K (approximate daylight).\n            Default: \"uniform\"\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The transform preserves the overall brightness of the image while shifting its color.\n        - The \"blackbody\" mode provides a wider range of color shifts, especially in the lower (warmer) temperatures.\n        - The \"cied\" mode is based on standard illuminants and may provide more realistic daylight variations.\n        - The Gaussian sampling method tends to produce more subtle variations, as it's centered around daylight.\n        - Unlike ColorJitter, this transform ensures that color changes are physically plausible and correlated\n          across channels, maintaining the natural appearance of the scene under different lighting conditions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PlanckianJitter(mode=\"blackbody\",\n        ...                               temperature_range=(3000, 9000),\n        ...                               sampling_method=\"uniform\",\n        ...                               p=1.0)\n        >>> result = transform(image=image)\n        >>> jittered_image = result[\"image\"]\n\n    References:\n        - Planck's law: https://en.wikipedia.org/wiki/Planck%27s_law\n        - CIE Standard Illuminants: https://en.wikipedia.org/wiki/Standard_illuminant\n        - Color temperature: https://en.wikipedia.org/wiki/Color_temperature\n        - Implementation inspired by: https://github.com/TheZino/PlanckianJitter\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mode: Literal[\"blackbody\", \"cied\"]\n        temperature_limit: Annotated[tuple[int, int], AfterValidator(nondecreasing)] | None\n        sampling_method: Literal[\"uniform\", \"gaussian\"]\n\n        @model_validator(mode=\"after\")\n        def validate_temperature(self) -> Self:\n            max_temp = int(PLANKIAN_JITTER_CONST[\"MAX_TEMP\"])\n\n            if self.temperature_limit is None:\n                if self.mode == \"blackbody\":\n                    self.temperature_limit = (\n                        int(PLANKIAN_JITTER_CONST[\"MIN_BLACKBODY_TEMP\"]),\n                        max_temp,\n                    )\n                elif self.mode == \"cied\":\n                    self.temperature_limit = (\n                        int(PLANKIAN_JITTER_CONST[\"MIN_CIED_TEMP\"]),\n                        max_temp,\n                    )\n            else:\n                if self.mode == \"blackbody\" and (\n                    min(self.temperature_limit) < PLANKIAN_JITTER_CONST[\"MIN_BLACKBODY_TEMP\"]\n                    or max(self.temperature_limit) > max_temp\n                ):\n                    raise ValueError(\n                        \"Temperature limits for blackbody should be in [3000, 15000] range\",\n                    )\n                if self.mode == \"cied\" and (\n                    min(self.temperature_limit) < PLANKIAN_JITTER_CONST[\"MIN_CIED_TEMP\"]\n                    or max(self.temperature_limit) > max_temp\n                ):\n                    raise ValueError(\n                        \"Temperature limits for CIED should be in [4000, 15000] range\",\n                    )\n\n                if not self.temperature_limit[0] <= PLANKIAN_JITTER_CONST[\"WHITE_TEMP\"] <= self.temperature_limit[1]:\n                    raise ValueError(\n                        \"White temperature should be within the temperature limits\",\n                    )\n\n            return self\n\n    def __init__(\n        self,\n        mode: Literal[\"blackbody\", \"cied\"] = \"blackbody\",\n        temperature_limit: tuple[int, int] | None = None,\n        sampling_method: Literal[\"uniform\", \"gaussian\"] = \"uniform\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ) -> None:\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.mode = mode\n        self.temperature_limit = cast(tuple[int, int], temperature_limit)\n        self.sampling_method = sampling_method\n\n    def apply(self, img: np.ndarray, temperature: int, **params: Any) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.planckian_jitter(img, temperature, mode=self.mode)\n\n    def get_params(self) -> dict[str, Any]:\n        sampling_prob_boundary = PLANKIAN_JITTER_CONST[\"SAMPLING_TEMP_PROB\"]\n        sampling_temp_boundary = PLANKIAN_JITTER_CONST[\"WHITE_TEMP\"]\n\n        if self.sampling_method == \"uniform\":\n            # Split into 2 cases to avoid selecting cold temperatures (>6000) too often\n            if self.py_random.random() < sampling_prob_boundary:\n                temperature = self.py_random.uniform(\n                    self.temperature_limit[0],\n                    sampling_temp_boundary,\n                )\n            else:\n                temperature = self.py_random.uniform(\n                    sampling_temp_boundary,\n                    self.temperature_limit[1],\n                )\n        elif self.sampling_method == \"gaussian\":\n            # Sample values from asymmetric gaussian distribution\n            if self.py_random.random() < sampling_prob_boundary:\n                # Left side\n                shift = np.abs(\n                    self.py_random.gauss(\n                        0,\n                        np.abs(sampling_temp_boundary - self.temperature_limit[0]) / 3,\n                    ),\n                )\n                temperature = sampling_temp_boundary - shift\n            else:\n                # Right side\n                shift = np.abs(\n                    self.py_random.gauss(\n                        0,\n                        np.abs(self.temperature_limit[1] - sampling_temp_boundary) / 3,\n                    ),\n                )\n                temperature = sampling_temp_boundary + shift\n        else:\n            raise ValueError(f\"Unknown sampling method: {self.sampling_method}\")\n\n        # Ensure temperature is within the valid range\n        temperature = np.clip(\n            temperature,\n            self.temperature_limit[0],\n            self.temperature_limit[1],\n        )\n\n        return {\"temperature\": int(temperature)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"mode\", \"temperature_limit\", \"sampling_method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlasmaBrightnessContrast","title":"class PlasmaBrightnessContrast (brightness_range=(-0.3, 0.3), contrast_range=(-0.3, 0.3), plasma_size=256, roughness=3.0, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply plasma fractal pattern to modify image brightness and contrast.

This transform uses the Diamond-Square algorithm to generate organic-looking fractal patterns that are then used to create spatially-varying brightness and contrast adjustments. The result is a natural-looking, non-uniform modification of the image.

Parameters:

Name Type Description brightness_range float, float

Range for brightness adjustment strength. Values between -1 and 1: - Positive values increase brightness - Negative values decrease brightness - 0 means no brightness change Default: (-0.3, 0.3)

contrast_range float, float

Range for contrast adjustment strength. Values between -1 and 1: - Positive values increase contrast - Negative values decrease contrast - 0 means no contrast change Default: (-0.3, 0.3)

plasma_size int

Size of the plasma pattern. Will be rounded up to nearest power of 2. Larger values create more detailed patterns. Default: 256

roughness float

Controls the roughness of the plasma pattern. Higher values create more rough/sharp transitions. Must be greater than 0. Typical values are between 1.0 and 5.0. Default: 3.0

p (float): Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Mathematical Formulation: 1. Plasma Pattern Generation: The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] by: - Starting with random corner values - Recursively computing midpoints using: M = (V1 + V2 + V3 + V4)/4 + R(d) where V1..V4 are corner values and R(d) is random noise that decreases with distance d according to the roughness parameter.

2. Brightness Adjustment:\n   For each pixel (x,y):\n   O(x,y) = I(x,y) + b\u00b7P(x,y)\u00b7max_value\n   where:\n   - I is the input image\n   - b is the brightness factor\n   - P is the plasma pattern\n   - max_value is the maximum possible pixel value\n\n3. Contrast Adjustment:\n   For each pixel (x,y):\n   O(x,y) = \u03bc + (I(x,y) - \u03bc)\u00b7(1 + c\u00b7P(x,y))\n   where:\n   - \u03bc is the mean pixel value\n   - c is the contrast factor\n   - P is the plasma pattern\n

Note

  • The plasma pattern creates smooth, organic variations in the adjustments
  • Brightness and contrast modifications are applied sequentially
  • Final values are clipped to valid range [0, max_value]
  • The same plasma pattern is used for both brightness and contrast to maintain coherent spatial variations

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-parameters","title":"Default parameters","text":"Python
>>> transform = A.PlasmaBrightnessContrast(p=1.0)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-adjustments-with-fine-pattern","title":"Custom adjustments with fine pattern","text":"Python
>>> transform = A.PlasmaBrightnessContrast(\n...     brightness_range=(-0.5, 0.5),\n...     contrast_range=(-0.3, 0.3),\n...     plasma_size=512,  # More detailed pattern\n...     roughness=2.5,    # Smoother transitions\n...     p=1.0\n... )\n

References

.. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\" Communications of the ACM, 1982. Paper introducing the Diamond-Square algorithm.

.. [2] Miller, \"The Diamond-Square Algorithm: A Detailed Analysis,\" Journal of Computer Graphics Techniques, 2016. Comprehensive analysis of the algorithm and its properties.

.. [3] Ebert et al., \"Texturing & Modeling: A Procedural Approach,\" Chapter 12: Noise, Hypertexture, Antialiasing, and Gesture. Detailed coverage of procedural noise patterns.

.. [4] Diamond-Square algorithm: https://en.wikipedia.org/wiki/Diamond-square_algorithm

.. [5] Plasma effect: https://lodev.org/cgtutor/plasma.html

See Also: - RandomBrightnessContrast: For uniform brightness/contrast adjustments - CLAHE: For contrast limited adaptive histogram equalization - FancyPCA: For color-based contrast enhancement - HistogramMatching: For reference-based contrast adjustment

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlasmaBrightnessContrast(ImageOnlyTransform):\n    \"\"\"Apply plasma fractal pattern to modify image brightness and contrast.\n\n    This transform uses the Diamond-Square algorithm to generate organic-looking fractal patterns\n    that are then used to create spatially-varying brightness and contrast adjustments.\n    The result is a natural-looking, non-uniform modification of the image.\n\n    Args:\n        brightness_range ((float, float)): Range for brightness adjustment strength.\n            Values between -1 and 1:\n            - Positive values increase brightness\n            - Negative values decrease brightness\n            - 0 means no brightness change\n            Default: (-0.3, 0.3)\n\n        contrast_range ((float, float)): Range for contrast adjustment strength.\n            Values between -1 and 1:\n            - Positive values increase contrast\n            - Negative values decrease contrast\n            - 0 means no contrast change\n            Default: (-0.3, 0.3)\n\n        plasma_size (int): Size of the plasma pattern. Will be rounded up to nearest power of 2.\n            Larger values create more detailed patterns. Default: 256\n\n        roughness (float): Controls the roughness of the plasma pattern.\n            Higher values create more rough/sharp transitions.\n            Must be greater than 0.\n            Typical values are between 1.0 and 5.0. Default: 3.0\n\n            p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Mathematical Formulation:\n        1. Plasma Pattern Generation:\n           The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] by:\n           - Starting with random corner values\n           - Recursively computing midpoints using:\n             M = (V1 + V2 + V3 + V4)/4 + R(d)\n           where V1..V4 are corner values and R(d) is random noise that\n           decreases with distance d according to the roughness parameter.\n\n        2. Brightness Adjustment:\n           For each pixel (x,y):\n           O(x,y) = I(x,y) + b\u00b7P(x,y)\u00b7max_value\n           where:\n           - I is the input image\n           - b is the brightness factor\n           - P is the plasma pattern\n           - max_value is the maximum possible pixel value\n\n        3. Contrast Adjustment:\n           For each pixel (x,y):\n           O(x,y) = \u03bc + (I(x,y) - \u03bc)\u00b7(1 + c\u00b7P(x,y))\n           where:\n           - \u03bc is the mean pixel value\n           - c is the contrast factor\n           - P is the plasma pattern\n\n    Note:\n        - The plasma pattern creates smooth, organic variations in the adjustments\n        - Brightness and contrast modifications are applied sequentially\n        - Final values are clipped to valid range [0, max_value]\n        - The same plasma pattern is used for both brightness and contrast\n          to maintain coherent spatial variations\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Default parameters\n        >>> transform = A.PlasmaBrightnessContrast(p=1.0)\n\n        # Custom adjustments with fine pattern\n        >>> transform = A.PlasmaBrightnessContrast(\n        ...     brightness_range=(-0.5, 0.5),\n        ...     contrast_range=(-0.3, 0.3),\n        ...     plasma_size=512,  # More detailed pattern\n        ...     roughness=2.5,    # Smoother transitions\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\"\n               Communications of the ACM, 1982.\n               Paper introducing the Diamond-Square algorithm.\n\n        .. [2] Miller, \"The Diamond-Square Algorithm: A Detailed Analysis,\"\n               Journal of Computer Graphics Techniques, 2016.\n               Comprehensive analysis of the algorithm and its properties.\n\n        .. [3] Ebert et al., \"Texturing & Modeling: A Procedural Approach,\"\n               Chapter 12: Noise, Hypertexture, Antialiasing, and Gesture.\n               Detailed coverage of procedural noise patterns.\n\n        .. [4] Diamond-Square algorithm:\n               https://en.wikipedia.org/wiki/Diamond-square_algorithm\n\n        .. [5] Plasma effect:\n               https://lodev.org/cgtutor/plasma.html\n\n    See Also:\n        - RandomBrightnessContrast: For uniform brightness/contrast adjustments\n        - CLAHE: For contrast limited adaptive histogram equalization\n        - FancyPCA: For color-based contrast enhancement\n        - HistogramMatching: For reference-based contrast adjustment\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n        ]\n        contrast_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(-1, 1)),\n        ]\n        plasma_size: int = Field(default=256, gt=0)\n        roughness: float = Field(default=3.0, gt=0)\n\n    def __init__(\n        self,\n        brightness_range: tuple[float, float] = (-0.3, 0.3),\n        contrast_range: tuple[float, float] = (-0.3, 0.3),\n        plasma_size: int = 256,\n        roughness: float = 3.0,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.brightness_range = brightness_range\n        self.contrast_range = contrast_range\n        self.plasma_size = plasma_size\n        self.roughness = roughness\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample adjustment strengths\n        brightness = self.py_random.uniform(*self.brightness_range)\n        contrast = self.py_random.uniform(*self.contrast_range)\n\n        # Generate plasma pattern\n        plasma = fmain.generate_plasma_pattern(\n            target_shape=image.shape[:2],\n            size=self.plasma_size,\n            roughness=self.roughness,\n            random_generator=self.random_generator,\n        )\n\n        return {\n            \"brightness_factor\": brightness,\n            \"contrast_factor\": contrast,\n            \"plasma_pattern\": plasma,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        brightness_factor: float,\n        contrast_factor: float,\n        plasma_pattern: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_plasma_brightness_contrast(\n            img,\n            brightness_factor,\n            contrast_factor,\n            plasma_pattern,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"brightness_range\", \"contrast_range\", \"plasma_size\", \"roughness\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.PlasmaShadow","title":"class PlasmaShadow (shadow_intensity_range=(0.3, 0.7), plasma_size=256, roughness=3.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply plasma-based shadow effect to the image.

Creates organic-looking shadows using plasma fractal noise pattern. The shadow intensity varies smoothly across the image, creating natural-looking darkening effects that can simulate shadows, shading, or lighting variations.

Parameters:

Name Type Description shadow_intensity_range tuple[float, float]

Range for shadow intensity. Values between 0 and 1: - 0 means no shadow (original image) - 1 means maximum darkening (black) - Values between create partial shadows Default: (0.3, 0.7)

plasma_size int

Size of the plasma pattern. Will be rounded up to nearest power of 2. Larger values create more detailed shadow patterns: - Small values (~64): Large, smooth shadow regions - Medium values (~256): Balanced detail level - Large values (~512+): Fine shadow details Default: 256

roughness float

Controls the roughness of the plasma pattern. Higher values create more rough/sharp shadow transitions. Must be greater than 0: - Low values (~1.0): Very smooth transitions - Medium values (~3.0): Natural-looking shadows - High values (~5.0): More dramatic, sharp shadows Default: 3.0

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • The transform darkens the image using a plasma pattern
  • Works with any number of channels (grayscale, RGB, multispectral)
  • Shadow pattern is generated using Diamond-Square algorithm
  • The same shadow pattern is applied to all channels
  • Final values are clipped to valid range [0, max_value]

Mathematical Formulation: 1. Plasma Pattern Generation: The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1] with fractal characteristics controlled by roughness parameter.

2. Shadow Application:\n   For each pixel (x,y):\n   O(x,y) = I(x,y) * (1 - i\u00b7P(x,y))\n   where:\n   - I is the input image\n   - P is the plasma pattern\n   - i is the shadow intensity\n   - O is the output image\n

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-parameters-for-natural-shadows","title":"Default parameters for natural shadows","text":"Python
>>> transform = A.PlasmaShadow(p=1.0)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--subtle-smooth-shadows","title":"Subtle, smooth shadows","text":"Python
>>> transform = A.PlasmaShadow(\n...     shadow_intensity=(0.1, 0.3),\n...     plasma_size=128,\n...     roughness=1.5,\n...     p=1.0\n... )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--dramatic-detailed-shadows","title":"Dramatic, detailed shadows","text":"Python
>>> transform = A.PlasmaShadow(\n...     shadow_intensity=(0.5, 0.9),\n...     plasma_size=512,\n...     roughness=4.0,\n...     p=1.0\n... )\n

References

.. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\" Communications of the ACM, 1982. Paper introducing the Diamond-Square algorithm.

.. [2] Diamond-Square algorithm: https://en.wikipedia.org/wiki/Diamond-square_algorithm

See Also: - PlasmaBrightnessContrast: For brightness/contrast adjustments using plasma patterns - RandomShadow: For geometric shadow effects - RandomToneCurve: For global lighting adjustments

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class PlasmaShadow(ImageOnlyTransform):\n    \"\"\"Apply plasma-based shadow effect to the image.\n\n    Creates organic-looking shadows using plasma fractal noise pattern.\n    The shadow intensity varies smoothly across the image, creating natural-looking\n    darkening effects that can simulate shadows, shading, or lighting variations.\n\n    Args:\n        shadow_intensity_range (tuple[float, float]): Range for shadow intensity.\n            Values between 0 and 1:\n            - 0 means no shadow (original image)\n            - 1 means maximum darkening (black)\n            - Values between create partial shadows\n            Default: (0.3, 0.7)\n\n        plasma_size (int): Size of the plasma pattern. Will be rounded up to nearest power of 2.\n            Larger values create more detailed shadow patterns:\n            - Small values (~64): Large, smooth shadow regions\n            - Medium values (~256): Balanced detail level\n            - Large values (~512+): Fine shadow details\n            Default: 256\n\n        roughness (float): Controls the roughness of the plasma pattern.\n            Higher values create more rough/sharp shadow transitions.\n            Must be greater than 0:\n            - Low values (~1.0): Very smooth transitions\n            - Medium values (~3.0): Natural-looking shadows\n            - High values (~5.0): More dramatic, sharp shadows\n            Default: 3.0\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform darkens the image using a plasma pattern\n        - Works with any number of channels (grayscale, RGB, multispectral)\n        - Shadow pattern is generated using Diamond-Square algorithm\n        - The same shadow pattern is applied to all channels\n        - Final values are clipped to valid range [0, max_value]\n\n    Mathematical Formulation:\n        1. Plasma Pattern Generation:\n           The Diamond-Square algorithm generates a pattern P(x,y) \u2208 [0,1]\n           with fractal characteristics controlled by roughness parameter.\n\n        2. Shadow Application:\n           For each pixel (x,y):\n           O(x,y) = I(x,y) * (1 - i\u00b7P(x,y))\n           where:\n           - I is the input image\n           - P is the plasma pattern\n           - i is the shadow intensity\n           - O is the output image\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Default parameters for natural shadows\n        >>> transform = A.PlasmaShadow(p=1.0)\n\n        # Subtle, smooth shadows\n        >>> transform = A.PlasmaShadow(\n        ...     shadow_intensity=(0.1, 0.3),\n        ...     plasma_size=128,\n        ...     roughness=1.5,\n        ...     p=1.0\n        ... )\n\n        # Dramatic, detailed shadows\n        >>> transform = A.PlasmaShadow(\n        ...     shadow_intensity=(0.5, 0.9),\n        ...     plasma_size=512,\n        ...     roughness=4.0,\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] Fournier, Fussell, and Carpenter, \"Computer rendering of stochastic models,\"\n               Communications of the ACM, 1982.\n               Paper introducing the Diamond-Square algorithm.\n\n        .. [2] Diamond-Square algorithm:\n               https://en.wikipedia.org/wiki/Diamond-square_algorithm\n\n    See Also:\n        - PlasmaBrightnessContrast: For brightness/contrast adjustments using plasma patterns\n        - RandomShadow: For geometric shadow effects\n        - RandomToneCurve: For global lighting adjustments\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        shadow_intensity_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        plasma_size: int = Field(default=256, gt=0)\n        roughness: float = Field(default=3.0, gt=0)\n\n    def __init__(\n        self,\n        shadow_intensity_range: tuple[float, float] = (0.3, 0.7),\n        plasma_size: int = 256,\n        roughness: float = 3.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.shadow_intensity_range = shadow_intensity_range\n        self.plasma_size = plasma_size\n        self.roughness = roughness\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample shadow intensity\n        intensity = self.py_random.uniform(*self.shadow_intensity_range)\n\n        # Generate plasma pattern\n        plasma = fmain.generate_plasma_pattern(\n            target_shape=image.shape[:2],\n            size=self.plasma_size,\n            roughness=self.roughness,\n            random_generator=self.random_generator,\n        )\n\n        return {\n            \"intensity\": intensity,\n            \"plasma_pattern\": plasma,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        intensity: float,\n        plasma_pattern: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_plasma_shadow(img, intensity, plasma_pattern)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"shadow_intensity_range\", \"plasma_size\", \"roughness\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Posterize","title":"class Posterize (num_bits=4, p=0.5, always_apply=None) [view source on GitHub]","text":"

Reduces the number of bits for each color channel in the image.

This transform applies color posterization, a technique that reduces the number of distinct colors used in an image. It works by lowering the number of bits used to represent each color channel, effectively creating a \"poster-like\" effect with fewer color gradations.

Parameters:

Name Type Description num_bits int | tuple[int, int] | list[int] | list[tuple[int, int]]

Defines the number of bits to keep for each color channel. Can be specified in several ways: - Single int: Same number of bits for all channels. Range: [1, 7]. - tuple of two ints: (min_bits, max_bits) to randomly choose from. Range for each: [1, 7]. - list of three ints: Specific number of bits for each channel [r_bits, g_bits, b_bits]. - list of three tuples: Ranges for each channel [(r_min, r_max), (g_min, g_max), (b_min, b_max)]. Default: 4

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effect becomes more pronounced as the number of bits is reduced.
  • This transform can create interesting artistic effects or be used for image compression simulation.
  • Posterization is particularly useful for:
  • Creating stylized or retro-looking images
  • Reducing the color palette for specific artistic effects
  • Simulating the look of older or lower-quality digital images
  • Data augmentation in scenarios where color depth might vary

Mathematical Background: For an 8-bit color channel, posterization to n bits can be expressed as: new_value = (old_value >> (8 - n)) << (8 - n) This operation keeps the n most significant bits and sets the rest to zero.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--posterize-all-channels-to-3-bits","title":"Posterize all channels to 3 bits","text":"Python
>>> transform = A.Posterize(num_bits=3, p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--randomly-posterize-between-2-and-5-bits","title":"Randomly posterize between 2 and 5 bits","text":"Python
>>> transform = A.Posterize(num_bits=(2, 5), p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--different-bits-for-each-channel","title":"Different bits for each channel","text":"Python
>>> transform = A.Posterize(num_bits=[3, 5, 2], p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--range-of-bits-for-each-channel","title":"Range of bits for each channel","text":"Python
>>> transform = A.Posterize(num_bits=[(1, 3), (3, 5), (2, 4)], p=1.0)\n>>> posterized_image = transform(image=image)[\"image\"]\n

References

  • Color Quantization: https://en.wikipedia.org/wiki/Color_quantization
  • Posterization: https://en.wikipedia.org/wiki/Posterization

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Posterize(ImageOnlyTransform):\n    \"\"\"Reduces the number of bits for each color channel in the image.\n\n    This transform applies color posterization, a technique that reduces the number of distinct\n    colors used in an image. It works by lowering the number of bits used to represent each\n    color channel, effectively creating a \"poster-like\" effect with fewer color gradations.\n\n    Args:\n        num_bits (int | tuple[int, int] | list[int] | list[tuple[int, int]]):\n            Defines the number of bits to keep for each color channel. Can be specified in several ways:\n            - Single int: Same number of bits for all channels. Range: [1, 7].\n            - tuple of two ints: (min_bits, max_bits) to randomly choose from. Range for each: [1, 7].\n            - list of three ints: Specific number of bits for each channel [r_bits, g_bits, b_bits].\n            - list of three tuples: Ranges for each channel [(r_min, r_max), (g_min, g_max), (b_min, b_max)].\n            Default: 4\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effect becomes more pronounced as the number of bits is reduced.\n        - This transform can create interesting artistic effects or be used for image compression simulation.\n        - Posterization is particularly useful for:\n          * Creating stylized or retro-looking images\n          * Reducing the color palette for specific artistic effects\n          * Simulating the look of older or lower-quality digital images\n          * Data augmentation in scenarios where color depth might vary\n\n    Mathematical Background:\n        For an 8-bit color channel, posterization to n bits can be expressed as:\n        new_value = (old_value >> (8 - n)) << (8 - n)\n        This operation keeps the n most significant bits and sets the rest to zero.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Posterize all channels to 3 bits\n        >>> transform = A.Posterize(num_bits=3, p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Randomly posterize between 2 and 5 bits\n        >>> transform = A.Posterize(num_bits=(2, 5), p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Different bits for each channel\n        >>> transform = A.Posterize(num_bits=[3, 5, 2], p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n        # Range of bits for each channel\n        >>> transform = A.Posterize(num_bits=[(1, 3), (3, 5), (2, 4)], p=1.0)\n        >>> posterized_image = transform(image=image)[\"image\"]\n\n    References:\n        - Color Quantization: https://en.wikipedia.org/wiki/Color_quantization\n        - Posterization: https://en.wikipedia.org/wiki/Posterization\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_bits: int | tuple[int, int] | list[tuple[int, int]]\n\n        @field_validator(\"num_bits\")\n        @classmethod\n        def validate_num_bits(\n            cls,\n            num_bits: Any,\n        ) -> tuple[int, int] | list[tuple[int, int]]:\n            if isinstance(num_bits, int):\n                if num_bits < 1 or num_bits > SEVEN:\n                    raise ValueError(\"num_bits must be in the range [1, 7]\")\n                return (num_bits, num_bits)\n            if isinstance(num_bits, Sequence) and len(num_bits) > PAIR:\n                return [to_tuple(i, i) for i in num_bits]\n            return cast(tuple[int, int], to_tuple(num_bits, num_bits))\n\n    def __init__(\n        self,\n        num_bits: int | tuple[int, int] | list[tuple[int, int]] = 4,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_bits = cast(Union[tuple[int, int], list[tuple[int, int]]], num_bits)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        num_bits: Literal[1, 2, 3, 4, 5, 6, 7] | list[Literal[1, 2, 3, 4, 5, 6, 7]],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.posterize(img, num_bits)\n\n    def get_params(self) -> dict[str, Any]:\n        if isinstance(self.num_bits, list):\n            num_bits = [self.py_random.randint(*i) for i in self.num_bits]\n            return {\"num_bits\": num_bits}\n        return {\"num_bits\": self.py_random.randint(*self.num_bits)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"num_bits\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RGBShift","title":"class RGBShift (r_shift_limit=(-20, 20), g_shift_limit=(-20, 20), b_shift_limit=(-20, 20), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shift values for each channel of the input RGB image.

A specialized version of AdditiveNoise that applies constant uniform shifts to RGB channels. Each channel (R,G,B) can have its own shift range specified.

Parameters:

Name Type Description r_shift_limit int, int) or int

Range for shifting the red channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-r_shift_limit, r_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

g_shift_limit int, int) or int

Range for shifting the green channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-g_shift_limit, g_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

b_shift_limit int, int) or int

Range for shifting the blue channel. Options: - If tuple (min, max): Sample shift value from this range - If int: Sample shift value from (-b_shift_limit, b_shift_limit) - For uint8 images: Values represent absolute shifts in [0, 255] - For float images: Values represent relative shifts in [0, 1] Default: (-20, 20)

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • Values are shifted independently for each channel
  • For uint8 images:
    • Input ranges like (-20, 20) represent pixel value shifts
    • A shift of 20 means adding 20 to that channel
    • Final values are clipped to [0, 255]
  • For float32 images:
    • Input ranges like (-0.1, 0.1) represent relative shifts
    • A shift of 0.1 means adding 0.1 to that channel
    • Final values are clipped to [0, 1]

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--shift-rgb-channels-of-uint8-image","title":"Shift RGB channels of uint8 image","text":"Python
>>> transform = A.RGBShift(\n...     r_shift_limit=30,  # Will sample red shift from [-30, 30]\n...     g_shift_limit=(-20, 20),  # Will sample green shift from [-20, 20]\n...     b_shift_limit=(-10, 10),  # Will sample blue shift from [-10, 10]\n...     p=1.0\n... )\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> shifted = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--same-effect-using-additivenoise","title":"Same effect using AdditiveNoise","text":"Python
>>> transform = A.AdditiveNoise(\n...     noise_type=\"uniform\",\n...     spatial_mode=\"constant\",  # One value per channel\n...     noise_params={\n...         \"ranges\": [(-30/255, 30/255), (-20/255, 20/255), (-10/255, 10/255)]\n...     },\n...     p=1.0\n... )\n

See Also: - AdditiveNoise: More general noise transform with various options: * Different noise distributions (uniform, gaussian, laplace, beta) * Spatial modes (constant, per-pixel, shared) * Approximation for faster computation - RandomToneCurve: For non-linear color transformations - RandomBrightnessContrast: For combined brightness and contrast adjustments - PlankianJitter: For color temperature adjustments - HueSaturationValue: For HSV color space adjustments - ColorJitter: For combined brightness, contrast, saturation adjustments

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RGBShift(AdditiveNoise):\n    \"\"\"Randomly shift values for each channel of the input RGB image.\n\n    A specialized version of AdditiveNoise that applies constant uniform shifts to RGB channels.\n    Each channel (R,G,B) can have its own shift range specified.\n\n    Args:\n        r_shift_limit ((int, int) or int): Range for shifting the red channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-r_shift_limit, r_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        g_shift_limit ((int, int) or int): Range for shifting the green channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-g_shift_limit, g_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        b_shift_limit ((int, int) or int): Range for shifting the blue channel. Options:\n            - If tuple (min, max): Sample shift value from this range\n            - If int: Sample shift value from (-b_shift_limit, b_shift_limit)\n            - For uint8 images: Values represent absolute shifts in [0, 255]\n            - For float images: Values represent relative shifts in [0, 1]\n            Default: (-20, 20)\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Values are shifted independently for each channel\n        - For uint8 images:\n            * Input ranges like (-20, 20) represent pixel value shifts\n            * A shift of 20 means adding 20 to that channel\n            * Final values are clipped to [0, 255]\n        - For float32 images:\n            * Input ranges like (-0.1, 0.1) represent relative shifts\n            * A shift of 0.1 means adding 0.1 to that channel\n            * Final values are clipped to [0, 1]\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n\n        # Shift RGB channels of uint8 image\n        >>> transform = A.RGBShift(\n        ...     r_shift_limit=30,  # Will sample red shift from [-30, 30]\n        ...     g_shift_limit=(-20, 20),  # Will sample green shift from [-20, 20]\n        ...     b_shift_limit=(-10, 10),  # Will sample blue shift from [-10, 10]\n        ...     p=1.0\n        ... )\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> shifted = transform(image=image)[\"image\"]\n\n        # Same effect using AdditiveNoise\n        >>> transform = A.AdditiveNoise(\n        ...     noise_type=\"uniform\",\n        ...     spatial_mode=\"constant\",  # One value per channel\n        ...     noise_params={\n        ...         \"ranges\": [(-30/255, 30/255), (-20/255, 20/255), (-10/255, 10/255)]\n        ...     },\n        ...     p=1.0\n        ... )\n\n    See Also:\n        - AdditiveNoise: More general noise transform with various options:\n            * Different noise distributions (uniform, gaussian, laplace, beta)\n            * Spatial modes (constant, per-pixel, shared)\n            * Approximation for faster computation\n        - RandomToneCurve: For non-linear color transformations\n        - RandomBrightnessContrast: For combined brightness and contrast adjustments\n        - PlankianJitter: For color temperature adjustments\n        - HueSaturationValue: For HSV color space adjustments\n        - ColorJitter: For combined brightness, contrast, saturation adjustments\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        r_shift_limit: SymmetricRangeType\n        g_shift_limit: SymmetricRangeType\n        b_shift_limit: SymmetricRangeType\n\n    def __init__(\n        self,\n        r_shift_limit: ScaleFloatType = (-20, 20),\n        g_shift_limit: ScaleFloatType = (-20, 20),\n        b_shift_limit: ScaleFloatType = (-20, 20),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        # Convert RGB shift limits to normalized ranges if needed\n        def normalize_range(limit: tuple[float, float]) -> tuple[float, float]:\n            # If any value is > 1, assume uint8 range and normalize\n            if abs(limit[0]) > 1 or abs(limit[1]) > 1:\n                return (limit[0] / 255.0, limit[1] / 255.0)\n            return limit\n\n        ranges = [\n            normalize_range(cast(tuple[float, float], r_shift_limit)),\n            normalize_range(cast(tuple[float, float], g_shift_limit)),\n            normalize_range(cast(tuple[float, float], b_shift_limit)),\n        ]\n\n        # Initialize with fixed noise type and spatial mode\n        super().__init__(\n            noise_type=\"uniform\",\n            spatial_mode=\"constant\",\n            noise_params={\"ranges\": ranges},\n            approximation=1.0,\n            p=p,\n        )\n\n        # Store original limits for get_transform_init_args\n        self.r_shift_limit = cast(tuple[float, float], r_shift_limit)\n        self.g_shift_limit = cast(tuple[float, float], g_shift_limit)\n        self.b_shift_limit = cast(tuple[float, float], b_shift_limit)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"r_shift_limit\", \"g_shift_limit\", \"b_shift_limit\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomBrightnessContrast","title":"class RandomBrightnessContrast (brightness_limit=(-0.2, 0.2), contrast_limit=(-0.2, 0.2), brightness_by_max=True, ensure_safe_range=False, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly changes the brightness and contrast of the input image.

This transform adjusts the brightness and contrast of an image simultaneously, allowing for a wide range of lighting and contrast variations. It's particularly useful for data augmentation in computer vision tasks, helping models become more robust to different lighting conditions.

Parameters:

Name Type Description brightness_limit float | tuple[float, float]

Factor range for changing brightness. If a single float value is provided, the range will be (-brightness_limit, brightness_limit). Values should typically be in the range [-1.0, 1.0], where 0 means no change, 1.0 means maximum brightness, and -1.0 means minimum brightness. Default: (-0.2, 0.2).

contrast_limit float | tuple[float, float]

Factor range for changing contrast. If a single float value is provided, the range will be (-contrast_limit, contrast_limit). Values should typically be in the range [-1.0, 1.0], where 0 means no change, 1.0 means maximum increase in contrast, and -1.0 means maximum decrease in contrast. Default: (-0.2, 0.2).

brightness_by_max bool

If True, adjusts brightness by scaling pixel values up to the maximum value of the image's dtype. If False, uses the mean pixel value for adjustment. Default: True.

ensure_safe_range bool

If True, adjusts alpha and beta to prevent overflow/underflow. This ensures output values stay within the valid range for the image dtype without clipping. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The order of operation is: contrast adjustment, then brightness adjustment.
  • For uint8 images, the output is clipped to [0, 255] range.
  • For float32 images, the output is clipped to [0, 1] range.
  • The brightness_by_max parameter affects how brightness is adjusted:
  • If True, brightness adjustment is more pronounced and can lead to more saturated results.
  • If False, brightness adjustment is more subtle and preserves the overall lighting better.
  • This transform is useful for:
  • Simulating different lighting conditions
  • Enhancing low-light or overexposed images
  • Data augmentation to improve model robustness

Mathematical Formulation: Let a be the contrast adjustment factor and \u03b2 be the brightness adjustment factor. For each pixel value x: 1. Contrast adjustment: x' = clip((x - mean) * (1 + a) + mean) 2. Brightness adjustment: If brightness_by_max is True: x'' = clip(x' * (1 + \u03b2)) If brightness_by_max is False: x'' = clip(x' + \u03b2 * max_value) Where clip() ensures values stay within the valid range for the image dtype.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomBrightnessContrast(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-brightness-and-contrast-limits","title":"Custom brightness and contrast limits","text":"Python
>>> transform = A.RandomBrightnessContrast(\n...     brightness_limit=0.3,\n...     contrast_limit=0.3,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--adjust-brightness-based-on-mean-value","title":"Adjust brightness based on mean value","text":"Python
>>> transform = A.RandomBrightnessContrast(\n...     brightness_limit=0.2,\n...     contrast_limit=0.2,\n...     brightness_by_max=False,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Brightness: https://en.wikipedia.org/wiki/Brightness
  • Contrast: https://en.wikipedia.org/wiki/Contrast_(vision)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomBrightnessContrast(ImageOnlyTransform):\n    \"\"\"Randomly changes the brightness and contrast of the input image.\n\n    This transform adjusts the brightness and contrast of an image simultaneously, allowing for\n    a wide range of lighting and contrast variations. It's particularly useful for data augmentation\n    in computer vision tasks, helping models become more robust to different lighting conditions.\n\n    Args:\n        brightness_limit (float | tuple[float, float]): Factor range for changing brightness.\n            If a single float value is provided, the range will be (-brightness_limit, brightness_limit).\n            Values should typically be in the range [-1.0, 1.0], where 0 means no change,\n            1.0 means maximum brightness, and -1.0 means minimum brightness.\n            Default: (-0.2, 0.2).\n\n        contrast_limit (float | tuple[float, float]): Factor range for changing contrast.\n            If a single float value is provided, the range will be (-contrast_limit, contrast_limit).\n            Values should typically be in the range [-1.0, 1.0], where 0 means no change,\n            1.0 means maximum increase in contrast, and -1.0 means maximum decrease in contrast.\n            Default: (-0.2, 0.2).\n\n        brightness_by_max (bool): If True, adjusts brightness by scaling pixel values up to the\n            maximum value of the image's dtype. If False, uses the mean pixel value for adjustment.\n            Default: True.\n\n        ensure_safe_range (bool): If True, adjusts alpha and beta to prevent overflow/underflow.\n            This ensures output values stay within the valid range for the image dtype without clipping.\n            Default: False.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The order of operation is: contrast adjustment, then brightness adjustment.\n        - For uint8 images, the output is clipped to [0, 255] range.\n        - For float32 images, the output is clipped to [0, 1] range.\n        - The `brightness_by_max` parameter affects how brightness is adjusted:\n          * If True, brightness adjustment is more pronounced and can lead to more saturated results.\n          * If False, brightness adjustment is more subtle and preserves the overall lighting better.\n        - This transform is useful for:\n          * Simulating different lighting conditions\n          * Enhancing low-light or overexposed images\n          * Data augmentation to improve model robustness\n\n    Mathematical Formulation:\n        Let a be the contrast adjustment factor and \u03b2 be the brightness adjustment factor.\n        For each pixel value x:\n        1. Contrast adjustment: x' = clip((x - mean) * (1 + a) + mean)\n        2. Brightness adjustment:\n           If brightness_by_max is True:  x'' = clip(x' * (1 + \u03b2))\n           If brightness_by_max is False: x'' = clip(x' + \u03b2 * max_value)\n        Where clip() ensures values stay within the valid range for the image dtype.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomBrightnessContrast(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom brightness and contrast limits\n        >>> transform = A.RandomBrightnessContrast(\n        ...     brightness_limit=0.3,\n        ...     contrast_limit=0.3,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Adjust brightness based on mean value\n        >>> transform = A.RandomBrightnessContrast(\n        ...     brightness_limit=0.2,\n        ...     contrast_limit=0.2,\n        ...     brightness_by_max=False,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Brightness: https://en.wikipedia.org/wiki/Brightness\n        - Contrast: https://en.wikipedia.org/wiki/Contrast_(vision)\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        brightness_limit: SymmetricRangeType\n        contrast_limit: SymmetricRangeType\n        brightness_by_max: bool\n        ensure_safe_range: bool\n\n    def __init__(\n        self,\n        brightness_limit: ScaleFloatType = (-0.2, 0.2),\n        contrast_limit: ScaleFloatType = (-0.2, 0.2),\n        brightness_by_max: bool = True,\n        ensure_safe_range: bool = False,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.brightness_limit = cast(tuple[float, float], brightness_limit)\n        self.contrast_limit = cast(tuple[float, float], contrast_limit)\n        self.brightness_by_max = brightness_by_max\n        self.ensure_safe_range = ensure_safe_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha: float,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return albucore.multiply_add(img, alpha, beta, inplace=False)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, float]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample initial values\n        alpha = 1.0 + self.py_random.uniform(*self.contrast_limit)\n        beta = self.py_random.uniform(*self.brightness_limit)\n\n        max_value = MAX_VALUES_BY_DTYPE[image.dtype]\n        # Scale beta according to brightness_by_max setting\n        beta = beta * max_value if self.brightness_by_max else beta * np.mean(image)\n\n        # Clip values to safe ranges if needed\n        if self.ensure_safe_range:\n            alpha, beta = fmain.get_safe_brightness_contrast_params(\n                alpha,\n                beta,\n                max_value,\n            )\n\n        return {\n            \"alpha\": alpha,\n            \"beta\": beta,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"brightness_limit\",\n            \"contrast_limit\",\n            \"brightness_by_max\",\n            \"ensure_safe_range\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomFog","title":"class RandomFog (fog_coef_lower=None, fog_coef_upper=None, alpha_coef=0.08, fog_coef_range=(0.3, 1), always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates fog for the image by adding random fog-like artifacts.

This transform creates a fog effect by generating semi-transparent overlays that mimic the visual characteristics of fog. The fog intensity and distribution can be controlled to create various fog-like conditions.

Parameters:

Name Type Description fog_coef_range tuple[float, float]

Range for fog intensity coefficient. Should be in [0, 1] range.

alpha_coef float

Transparency of the fog circles. Should be in [0, 1] range. Default: 0.08.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The fog effect is created by overlaying semi-transparent circles on the image.
  • Higher fog coefficient values result in denser fog effects.
  • The fog is typically denser in the center of the image and gradually decreases towards the edges.
  • This transform is useful for:
  • Simulating various weather conditions in outdoor scenes
  • Data augmentation for improving model robustness to foggy conditions
  • Creating atmospheric effects in image editing

Mathematical Formulation: For each fog particle: 1. A position (x, y) is randomly generated within the image. 2. A circle with random radius is drawn at this position. 3. The circle's alpha (transparency) is determined by the alpha_coef. 4. These circles are overlaid on the original image to create the fog effect.

The final pixel value is calculated as:\noutput = (1 - alpha) * original_pixel + alpha * fog_color\n\nwhere alpha is influenced by the fog_coef and alpha_coef parameters.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomFog(p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-fog-intensity-range","title":"Custom fog intensity range","text":"Python
>>> transform = A.RandomFog(fog_coef_lower=0.3, fog_coef_upper=0.8, p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--adjust-fog-transparency","title":"Adjust fog transparency","text":"Python
>>> transform = A.RandomFog(fog_coef_lower=0.2, fog_coef_upper=0.5, alpha_coef=0.1, p=1.0)\n>>> foggy_image = transform(image=image)[\"image\"]\n

References

  • Fog: https://en.wikipedia.org/wiki/Fog
  • Atmospheric perspective: https://en.wikipedia.org/wiki/Aerial_perspective

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomFog(ImageOnlyTransform):\n    \"\"\"Simulates fog for the image by adding random fog-like artifacts.\n\n    This transform creates a fog effect by generating semi-transparent overlays\n    that mimic the visual characteristics of fog. The fog intensity and distribution\n    can be controlled to create various fog-like conditions.\n\n    Args:\n        fog_coef_range (tuple[float, float]): Range for fog intensity coefficient. Should be in [0, 1] range.\n        alpha_coef (float): Transparency of the fog circles. Should be in [0, 1] range. Default: 0.08.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The fog effect is created by overlaying semi-transparent circles on the image.\n        - Higher fog coefficient values result in denser fog effects.\n        - The fog is typically denser in the center of the image and gradually decreases towards the edges.\n        - This transform is useful for:\n          * Simulating various weather conditions in outdoor scenes\n          * Data augmentation for improving model robustness to foggy conditions\n          * Creating atmospheric effects in image editing\n\n    Mathematical Formulation:\n        For each fog particle:\n        1. A position (x, y) is randomly generated within the image.\n        2. A circle with random radius is drawn at this position.\n        3. The circle's alpha (transparency) is determined by the alpha_coef.\n        4. These circles are overlaid on the original image to create the fog effect.\n\n        The final pixel value is calculated as:\n        output = (1 - alpha) * original_pixel + alpha * fog_color\n\n        where alpha is influenced by the fog_coef and alpha_coef parameters.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomFog(p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n        # Custom fog intensity range\n        >>> transform = A.RandomFog(fog_coef_lower=0.3, fog_coef_upper=0.8, p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n        # Adjust fog transparency\n        >>> transform = A.RandomFog(fog_coef_lower=0.2, fog_coef_upper=0.5, alpha_coef=0.1, p=1.0)\n        >>> foggy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Fog: https://en.wikipedia.org/wiki/Fog\n        - Atmospheric perspective: https://en.wikipedia.org/wiki/Aerial_perspective\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        fog_coef_lower: float | None = Field(\n            ge=0,\n            le=1,\n        )\n        fog_coef_upper: float | None = Field(\n            ge=0,\n            le=1,\n        )\n        fog_coef_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        alpha_coef: float = Field(ge=0, le=1)\n\n        @model_validator(mode=\"after\")\n        def validate_fog_coefficients(self) -> Self:\n            if self.fog_coef_lower is not None:\n                warn(\n                    \"`fog_coef_lower` is deprecated, use `fog_coef_range` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            if self.fog_coef_upper is not None:\n                warn(\n                    \"`fog_coef_upper` is deprecated, use `fog_coef_range` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            lower = self.fog_coef_lower if self.fog_coef_lower is not None else self.fog_coef_range[0]\n            upper = self.fog_coef_upper if self.fog_coef_upper is not None else self.fog_coef_range[1]\n            self.fog_coef_range = (lower, upper)\n\n            self.fog_coef_lower = None\n            self.fog_coef_upper = None\n\n            return self\n\n    def __init__(\n        self,\n        fog_coef_lower: float | None = None,\n        fog_coef_upper: float | None = None,\n        alpha_coef: float = 0.08,\n        fog_coef_range: tuple[float, float] = (0.3, 1),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.fog_coef_range = fog_coef_range\n        self.alpha_coef = alpha_coef\n\n    def apply(\n        self,\n        img: np.ndarray,\n        particle_positions: list[tuple[int, int]],\n        radiuses: list[int],\n        intensity: float,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.add_fog(\n            img,\n            intensity,\n            self.alpha_coef,\n            particle_positions,\n            radiuses,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        # Select a random fog intensity within the specified range\n        intensity = self.py_random.uniform(*self.fog_coef_range)\n\n        image_shape = params[\"shape\"][:2]\n\n        image_height, image_width = image_shape\n\n        # Calculate the size of the fog effect region based on image width and fog intensity\n        fog_region_size = max(1, int(image_width // 3 * intensity))\n\n        particle_positions = []\n\n        # Initialize the central region where fog will be most dense\n        center_x, center_y = (int(x) for x in fgeometric.center(image_shape))\n\n        # Define the initial size of the foggy area\n        current_width = image_width\n        current_height = image_height\n\n        # Define shrink factor for reducing the foggy area each iteration\n        shrink_factor = 0.1\n\n        max_iterations = 10  # Prevent infinite loop\n        iteration = 0\n\n        while current_width > fog_region_size and current_height > fog_region_size and iteration < max_iterations:\n            # Calculate the number of particles for this region\n            area = current_width * current_height\n            particles_in_region = int(\n                area / (fog_region_size * fog_region_size) * intensity * 10,\n            )\n\n            for _ in range(particles_in_region):\n                # Generate random positions within the current region\n                x = self.py_random.randint(\n                    center_x - current_width // 2,\n                    center_x + current_width // 2,\n                )\n                y = self.py_random.randint(\n                    center_y - current_height // 2,\n                    center_y + current_height // 2,\n                )\n                particle_positions.append((x, y))\n\n            # Shrink the region for the next iteration\n            current_width = int(current_width * (1 - shrink_factor))\n            current_height = int(current_height * (1 - shrink_factor))\n\n            iteration += 1\n\n        radiuses = fmain.get_fog_particle_radiuses(\n            image_shape,\n            len(particle_positions),\n            intensity,\n            self.random_generator,\n        )\n\n        return {\n            \"particle_positions\": particle_positions,\n            \"intensity\": intensity,\n            \"radiuses\": radiuses,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"fog_coef_range\", \"alpha_coef\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomGamma","title":"class RandomGamma (gamma_limit=(80, 120), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies random gamma correction to the input image.

Gamma correction, or simply gamma, is a nonlinear operation used to encode and decode luminance or tristimulus values in imaging systems. This transform can adjust the brightness of an image while preserving the relative differences between darker and lighter areas, making it useful for simulating different lighting conditions or correcting for display characteristics.

Parameters:

Name Type Description gamma_limit float | tuple[float, float]

If gamma_limit is a single float value, the range will be (1, gamma_limit). If it's a tuple of two floats, they will serve as the lower and upper bounds for gamma adjustment. Values are in terms of percentage change, e.g., (80, 120) means the gamma will be between 80% and 120% of the original. Default: (80, 120).

eps

A small value added to the gamma to avoid division by zero or log of zero errors. Default: 1e-7.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • The gamma correction is applied using the formula: output = input^gamma
  • Gamma values > 1 will make the image darker, while values < 1 will make it brighter
  • This transform is particularly useful for:
  • Simulating different lighting conditions
  • Correcting for non-linear display characteristics
  • Enhancing contrast in certain regions of the image
  • Data augmentation in computer vision tasks

Mathematical Formulation: Let I be the input image and G (gamma) be the correction factor. The gamma correction is applied as follows: 1. Normalize the image to [0, 1] range: I_norm = I / 255 (for uint8 images) 2. Apply gamma correction: I_corrected = I_norm ^ (1 / G) 3. Scale back to original range: output = I_corrected * 255 (for uint8 images)

The actual gamma value used is calculated as:\nG = 1 + (random_value / 100), where random_value is sampled from gamma_limit range.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomGamma(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-gamma-range","title":"Custom gamma range","text":"Python
>>> transform = A.RandomGamma(gamma_limit=(50, 150), p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--applying-with-other-transforms","title":"Applying with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomGamma(gamma_limit=(80, 120), p=0.5),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction
  • Power law (Gamma) encoding: https://www.cambridgeincolour.com/tutorials/gamma-correction.htm

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomGamma(ImageOnlyTransform):\n    \"\"\"Applies random gamma correction to the input image.\n\n    Gamma correction, or simply gamma, is a nonlinear operation used to encode and decode luminance\n    or tristimulus values in imaging systems. This transform can adjust the brightness of an image\n    while preserving the relative differences between darker and lighter areas, making it useful\n    for simulating different lighting conditions or correcting for display characteristics.\n\n    Args:\n        gamma_limit (float | tuple[float, float]): If gamma_limit is a single float value, the range\n            will be (1, gamma_limit). If it's a tuple of two floats, they will serve as\n            the lower and upper bounds for gamma adjustment. Values are in terms of percentage change,\n            e.g., (80, 120) means the gamma will be between 80% and 120% of the original.\n            Default: (80, 120).\n        eps: A small value added to the gamma to avoid division by zero or log of zero errors.\n            Default: 1e-7.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The gamma correction is applied using the formula: output = input^gamma\n        - Gamma values > 1 will make the image darker, while values < 1 will make it brighter\n        - This transform is particularly useful for:\n          * Simulating different lighting conditions\n          * Correcting for non-linear display characteristics\n          * Enhancing contrast in certain regions of the image\n          * Data augmentation in computer vision tasks\n\n    Mathematical Formulation:\n        Let I be the input image and G (gamma) be the correction factor.\n        The gamma correction is applied as follows:\n        1. Normalize the image to [0, 1] range: I_norm = I / 255 (for uint8 images)\n        2. Apply gamma correction: I_corrected = I_norm ^ (1 / G)\n        3. Scale back to original range: output = I_corrected * 255 (for uint8 images)\n\n        The actual gamma value used is calculated as:\n        G = 1 + (random_value / 100), where random_value is sampled from gamma_limit range.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomGamma(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom gamma range\n        >>> transform = A.RandomGamma(gamma_limit=(50, 150), p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Applying with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomGamma(gamma_limit=(80, 120), p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction\n        - Power law (Gamma) encoding: https://www.cambridgeincolour.com/tutorials/gamma-correction.htm\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        gamma_limit: OnePlusFloatRangeType\n\n    def __init__(\n        self,\n        gamma_limit: ScaleFloatType = (80, 120),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.gamma_limit = cast(tuple[float, float], gamma_limit)\n\n    def apply(self, img: np.ndarray, gamma: float, **params: Any) -> np.ndarray:\n        return fmain.gamma_transform(img, gamma=gamma)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"gamma\": self.py_random.uniform(self.gamma_limit[0], self.gamma_limit[1]) / 100.0,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"gamma_limit\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomGravel","title":"class RandomGravel (gravel_roi=(0.1, 0.4, 0.9, 0.9), number_of_patches=2, always_apply=None, p=0.5) [view source on GitHub]","text":"

Adds gravel-like artifacts to the input image.

This transform simulates the appearance of gravel or small stones scattered across specific regions of an image. It's particularly useful for augmenting datasets of road or terrain images, adding realistic texture variations.

Parameters:

Name Type Description gravel_roi tuple[float, float, float, float]

Region of interest where gravel will be added, specified as (x_min, y_min, x_max, y_max) in relative coordinates [0, 1]. Default: (0.1, 0.4, 0.9, 0.9).

number_of_patches int

Number of gravel patch regions to generate within the ROI. Each patch will contain multiple gravel particles. Default: 2.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The gravel effect is created by modifying the saturation channel in the HLS color space.
  • Gravel particles are distributed within randomly generated patches inside the specified ROI.
  • This transform is particularly useful for:
  • Augmenting datasets for road condition analysis
  • Simulating variations in terrain for computer vision tasks
  • Adding realistic texture to synthetic images of outdoor scenes

Mathematical Formulation: For each gravel patch: 1. A rectangular region is randomly generated within the specified ROI. 2. Within this region, multiple gravel particles are placed. 3. For each particle: - Random (x, y) coordinates are generated within the patch. - A random radius (r) between 1 and 3 pixels is assigned. - A random saturation value (sat) between 0 and 255 is assigned. 4. The saturation channel of the image is modified for each particle: image_hls[y-r:y+r, x-r:x+r, 1] = sat

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomGravel(p=1.0)\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-roi-and-number-of-patches","title":"Custom ROI and number of patches","text":"Python
>>> transform = A.RandomGravel(\n...     gravel_roi=(0.2, 0.2, 0.8, 0.8),\n...     number_of_patches=5,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--combining-with-other-transforms","title":"Combining with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomGravel(p=0.7),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Road surface textures: https://en.wikipedia.org/wiki/Road_surface
  • HLS color space: https://en.wikipedia.org/wiki/HSL_and_HSV

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomGravel(ImageOnlyTransform):\n    \"\"\"Adds gravel-like artifacts to the input image.\n\n    This transform simulates the appearance of gravel or small stones scattered across\n    specific regions of an image. It's particularly useful for augmenting datasets of\n    road or terrain images, adding realistic texture variations.\n\n    Args:\n        gravel_roi (tuple[float, float, float, float]): Region of interest where gravel\n            will be added, specified as (x_min, y_min, x_max, y_max) in relative coordinates\n            [0, 1]. Default: (0.1, 0.4, 0.9, 0.9).\n        number_of_patches (int): Number of gravel patch regions to generate within the ROI.\n            Each patch will contain multiple gravel particles. Default: 2.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The gravel effect is created by modifying the saturation channel in the HLS color space.\n        - Gravel particles are distributed within randomly generated patches inside the specified ROI.\n        - This transform is particularly useful for:\n          * Augmenting datasets for road condition analysis\n          * Simulating variations in terrain for computer vision tasks\n          * Adding realistic texture to synthetic images of outdoor scenes\n\n    Mathematical Formulation:\n        For each gravel patch:\n        1. A rectangular region is randomly generated within the specified ROI.\n        2. Within this region, multiple gravel particles are placed.\n        3. For each particle:\n           - Random (x, y) coordinates are generated within the patch.\n           - A random radius (r) between 1 and 3 pixels is assigned.\n           - A random saturation value (sat) between 0 and 255 is assigned.\n        4. The saturation channel of the image is modified for each particle:\n           image_hls[y-r:y+r, x-r:x+r, 1] = sat\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomGravel(p=1.0)\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Custom ROI and number of patches\n        >>> transform = A.RandomGravel(\n        ...     gravel_roi=(0.2, 0.2, 0.8, 0.8),\n        ...     number_of_patches=5,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n        # Combining with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomGravel(p=0.7),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Road surface textures: https://en.wikipedia.org/wiki/Road_surface\n        - HLS color space: https://en.wikipedia.org/wiki/HSL_and_HSV\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        gravel_roi: tuple[float, float, float, float]\n        number_of_patches: int = Field(ge=1)\n\n        @model_validator(mode=\"after\")\n        def validate_gravel_roi(self) -> Self:\n            gravel_lower_x, gravel_lower_y, gravel_upper_x, gravel_upper_y = self.gravel_roi\n            if not 0 <= gravel_lower_x < gravel_upper_x <= 1 or not 0 <= gravel_lower_y < gravel_upper_y <= 1:\n                raise ValueError(f\"Invalid gravel_roi. Got: {self.gravel_roi}.\")\n            return self\n\n    def __init__(\n        self,\n        gravel_roi: tuple[float, float, float, float] = (0.1, 0.4, 0.9, 0.9),\n        number_of_patches: int = 2,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p, always_apply)\n        self.gravel_roi = gravel_roi\n        self.number_of_patches = number_of_patches\n\n    def generate_gravel_patch(\n        self,\n        rectangular_roi: tuple[int, int, int, int],\n    ) -> np.ndarray:\n        x_min, y_min, x_max, y_max = rectangular_roi\n        area = abs((x_max - x_min) * (y_max - y_min))\n        count = area // 10\n        gravels = np.empty([count, 2], dtype=np.int64)\n        gravels[:, 0] = self.random_generator.integers(x_min, x_max, count)\n        gravels[:, 1] = self.random_generator.integers(y_min, y_max, count)\n        return gravels\n\n    def apply(\n        self,\n        img: np.ndarray,\n        gravels_infos: list[Any],\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_gravel(img, gravels_infos)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n\n        # Calculate ROI in pixels\n        x_min, y_min, x_max, y_max = (\n            int(coord * dim) for coord, dim in zip(self.gravel_roi, [width, height, width, height])\n        )\n\n        roi_width = x_max - x_min\n        roi_height = y_max - y_min\n\n        gravels_info = []\n\n        for _ in range(self.number_of_patches):\n            # Generate a random rectangular region within the ROI\n            patch_width = self.py_random.randint(roi_width // 10, roi_width // 5)\n            patch_height = self.py_random.randint(roi_height // 10, roi_height // 5)\n\n            patch_x = self.py_random.randint(x_min, x_max - patch_width)\n            patch_y = self.py_random.randint(y_min, y_max - patch_height)\n\n            # Generate gravel particles within this patch\n            num_particles = (patch_width * patch_height) // 100  # Adjust this divisor to control density\n\n            for _ in range(num_particles):\n                x = self.py_random.randint(patch_x, patch_x + patch_width)\n                y = self.py_random.randint(patch_y, patch_y + patch_height)\n                r = self.py_random.randint(1, 3)\n                sat = self.py_random.randint(0, 255)\n\n                gravels_info.append(\n                    [\n                        max(y - r, 0),  # min_y\n                        min(y + r, height - 1),  # max_y\n                        max(x - r, 0),  # min_x\n                        min(x + r, width - 1),  # max_x\n                        sat,  # saturation\n                    ],\n                )\n\n        return {\"gravels_infos\": np.array(gravels_info, dtype=np.int64)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return \"gravel_roi\", \"number_of_patches\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomRain","title":"class RandomRain (slant_lower=None, slant_upper=None, slant_range=(-10, 10), drop_length=20, drop_width=1, drop_color=(200, 200, 200), blur_value=7, brightness_coefficient=0.7, rain_type='default', always_apply=None, p=0.5) [view source on GitHub]","text":"

Adds rain effects to an image.

This transform simulates rainfall by overlaying semi-transparent streaks onto the image, creating a realistic rain effect. It can be used to augment datasets for computer vision tasks that need to perform well in rainy conditions.

Parameters:

Name Type Description slant_range tuple[int, int]

Range for the rain slant angle in degrees. Negative values slant to the left, positive to the right. Default: (-10, 10).

drop_length int

Length of the rain drops in pixels. Default: 20.

drop_width int

Width of the rain drops in pixels. Default: 1.

drop_color tuple[int, int, int]

Color of the rain drops in RGB format. Default: (200, 200, 200).

blur_value int

Blur value for simulating rain effect. Rainy views are typically blurry. Default: 7.

brightness_coefficient float

Coefficient to adjust the brightness of the image. Rainy scenes are usually darker. Should be in the range (0, 1]. Default: 0.7.

rain_type Literal[\"drizzle\", \"heavy\", \"torrential\", \"default\"]

Type of rain to simulate.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

  • The rain effect is created by drawing semi-transparent lines on the image.
  • The slant of the rain can be controlled to simulate wind effects.
  • Different rain types (drizzle, heavy, torrential) adjust the density and appearance of the rain.
  • The transform also adjusts image brightness and applies a blur to simulate the visual effects of rain.
  • This transform is particularly useful for:
  • Augmenting datasets for autonomous driving in rainy conditions
  • Testing the robustness of computer vision models to weather effects
  • Creating realistic rainy scenes for image editing or film production

Mathematical Formulation: For each raindrop: 1. Start position (x1, y1) is randomly generated within the image. 2. End position (x2, y2) is calculated based on drop_length and slant: x2 = x1 + drop_length * sin(slant) y2 = y1 + drop_length * cos(slant) 3. A line is drawn from (x1, y1) to (x2, y2) with the specified drop_color and drop_width. 4. The image is then blurred and its brightness is adjusted.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomRain(p=1.0)\n>>> rainy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-rain-parameters","title":"Custom rain parameters","text":"Python
>>> transform = A.RandomRain(\n...     slant_range=(-15, 15),\n...     drop_length=30,\n...     drop_width=2,\n...     drop_color=(180, 180, 180),\n...     blur_value=5,\n...     brightness_coefficient=0.8,\n...     p=1.0\n... )\n>>> rainy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--simulating-heavy-rain","title":"Simulating heavy rain","text":"Python
>>> transform = A.RandomRain(rain_type=\"heavy\", p=1.0)\n>>> heavy_rain_image = transform(image=image)[\"image\"]\n

References

  • Rain visualization techniques: https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-27-real-time-rain-rendering
  • Weather effects in computer vision: https://www.sciencedirect.com/science/article/pii/S1077314220300692

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomRain(ImageOnlyTransform):\n    \"\"\"Adds rain effects to an image.\n\n    This transform simulates rainfall by overlaying semi-transparent streaks onto the image,\n    creating a realistic rain effect. It can be used to augment datasets for computer vision\n    tasks that need to perform well in rainy conditions.\n\n    Args:\n        slant_range (tuple[int, int]): Range for the rain slant angle in degrees.\n            Negative values slant to the left, positive to the right. Default: (-10, 10).\n        drop_length (int): Length of the rain drops in pixels. Default: 20.\n        drop_width (int): Width of the rain drops in pixels. Default: 1.\n        drop_color (tuple[int, int, int]): Color of the rain drops in RGB format. Default: (200, 200, 200).\n        blur_value (int): Blur value for simulating rain effect. Rainy views are typically blurry. Default: 7.\n        brightness_coefficient (float): Coefficient to adjust the brightness of the image.\n            Rainy scenes are usually darker. Should be in the range (0, 1]. Default: 0.7.\n        rain_type (Literal[\"drizzle\", \"heavy\", \"torrential\", \"default\"]): Type of rain to simulate.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        - The rain effect is created by drawing semi-transparent lines on the image.\n        - The slant of the rain can be controlled to simulate wind effects.\n        - Different rain types (drizzle, heavy, torrential) adjust the density and appearance of the rain.\n        - The transform also adjusts image brightness and applies a blur to simulate the visual effects of rain.\n        - This transform is particularly useful for:\n          * Augmenting datasets for autonomous driving in rainy conditions\n          * Testing the robustness of computer vision models to weather effects\n          * Creating realistic rainy scenes for image editing or film production\n\n    Mathematical Formulation:\n        For each raindrop:\n        1. Start position (x1, y1) is randomly generated within the image.\n        2. End position (x2, y2) is calculated based on drop_length and slant:\n           x2 = x1 + drop_length * sin(slant)\n           y2 = y1 + drop_length * cos(slant)\n        3. A line is drawn from (x1, y1) to (x2, y2) with the specified drop_color and drop_width.\n        4. The image is then blurred and its brightness is adjusted.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomRain(p=1.0)\n        >>> rainy_image = transform(image=image)[\"image\"]\n\n        # Custom rain parameters\n        >>> transform = A.RandomRain(\n        ...     slant_range=(-15, 15),\n        ...     drop_length=30,\n        ...     drop_width=2,\n        ...     drop_color=(180, 180, 180),\n        ...     blur_value=5,\n        ...     brightness_coefficient=0.8,\n        ...     p=1.0\n        ... )\n        >>> rainy_image = transform(image=image)[\"image\"]\n\n        # Simulating heavy rain\n        >>> transform = A.RandomRain(rain_type=\"heavy\", p=1.0)\n        >>> heavy_rain_image = transform(image=image)[\"image\"]\n\n    References:\n        - Rain visualization techniques: https://developer.nvidia.com/gpugems/gpugems3/part-iv-image-effects/chapter-27-real-time-rain-rendering\n        - Weather effects in computer vision: https://www.sciencedirect.com/science/article/pii/S1077314220300692\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        slant_lower: int | None = Field(default=None)\n        slant_upper: int | None = Field(default=None)\n        slant_range: Annotated[tuple[float, float], AfterValidator(nondecreasing)]\n        drop_length: int = Field(ge=1)\n        drop_width: int = Field(ge=1)\n        drop_color: tuple[int, int, int]\n        blur_value: int = Field(ge=1)\n        brightness_coefficient: float = Field(gt=0, le=1)\n        rain_type: RainMode\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            if self.slant_lower is not None or self.slant_upper is not None:\n                if self.slant_lower is not None:\n                    warn(\n                        \"`slant_lower` deprecated. Use `slant_range` as tuple (slant_lower, slant_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.slant_upper is not None:\n                    warn(\n                        \"`slant_upper` deprecated. Use `slant_range` as tuple (slant_lower, slant_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.slant_lower if self.slant_lower is not None else self.slant_range[0]\n                upper = self.slant_upper if self.slant_upper is not None else self.slant_range[1]\n                self.slant_range = (lower, upper)\n                self.slant_lower = None\n                self.slant_upper = None\n\n            # Validate the slant_range\n            if not (-MAX_RAIN_ANGLE <= self.slant_range[0] <= self.slant_range[1] <= MAX_RAIN_ANGLE):\n                raise ValueError(\n                    f\"slant_range values should be increasing within [-{MAX_RAIN_ANGLE}, {MAX_RAIN_ANGLE}] range.\",\n                )\n            return self\n\n    def __init__(\n        self,\n        slant_lower: int | None = None,\n        slant_upper: int | None = None,\n        slant_range: tuple[int, int] = (-10, 10),\n        drop_length: int = 20,\n        drop_width: int = 1,\n        drop_color: tuple[int, int, int] = (200, 200, 200),\n        blur_value: int = 7,\n        brightness_coefficient: float = 0.7,\n        rain_type: RainMode = \"default\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.slant_range = slant_range\n        self.drop_length = drop_length\n        self.drop_width = drop_width\n        self.drop_color = drop_color\n        self.blur_value = blur_value\n        self.brightness_coefficient = brightness_coefficient\n        self.rain_type = rain_type\n\n    def apply(\n        self,\n        img: np.ndarray,\n        slant: int,\n        drop_length: int,\n        rain_drops: list[tuple[int, int]],\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n\n        return fmain.add_rain(\n            img,\n            slant,\n            drop_length,\n            self.drop_width,\n            self.drop_color,\n            self.blur_value,\n            self.brightness_coefficient,\n            rain_drops,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        slant = int(self.py_random.uniform(*self.slant_range))\n\n        height, width = params[\"shape\"][:2]\n        area = height * width\n\n        if self.rain_type == \"drizzle\":\n            num_drops = area // 770\n            drop_length = 10\n        elif self.rain_type == \"heavy\":\n            num_drops = width * height // 600\n            drop_length = 30\n        elif self.rain_type == \"torrential\":\n            num_drops = area // 500\n            drop_length = 60\n        else:\n            drop_length = self.drop_length\n            num_drops = area // 600\n\n        rain_drops = []\n\n        for _ in range(num_drops):  # If You want heavy rain, try increasing this\n            x = self.py_random.randint(slant, width) if slant < 0 else self.py_random.randint(0, max(width - slant, 0))\n            y = self.py_random.randint(0, max(height - drop_length, 0))\n\n            rain_drops.append((x, y))\n\n        return {\"drop_length\": drop_length, \"slant\": slant, \"rain_drops\": rain_drops}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"slant_range\",\n            \"drop_length\",\n            \"drop_width\",\n            \"drop_color\",\n            \"blur_value\",\n            \"brightness_coefficient\",\n            \"rain_type\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomShadow","title":"class RandomShadow (shadow_roi=(0, 0.5, 1, 1), num_shadows_limit=(1, 2), num_shadows_lower=None, num_shadows_upper=None, shadow_dimension=5, shadow_intensity_range=(0.5, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates shadows for the image by reducing the brightness of the image in shadow regions.

This transform adds realistic shadow effects to images, which can be useful for augmenting datasets for outdoor scene analysis, autonomous driving, or any computer vision task where shadows may be present.

Parameters:

Name Type Description shadow_roi tuple[float, float, float, float]

Region of the image where shadows will appear (x_min, y_min, x_max, y_max). All values should be in range [0, 1]. Default: (0, 0.5, 1, 1).

num_shadows_limit tuple[int, int]

Lower and upper limits for the possible number of shadows. Default: (1, 2).

shadow_dimension int

Number of edges in the shadow polygons. Default: 5.

shadow_intensity_range tuple[float, float]

Range for the shadow intensity. Larger value means darker shadow. Should be two float values between 0 and 1. Default: (0.5, 0.5).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • Shadows are created by generating random polygons within the specified ROI and reducing the brightness of the image in these areas.
  • The number of shadows, their shapes, and intensities can be randomized for variety.
  • This transform is particularly useful for:
  • Augmenting datasets for outdoor scene understanding
  • Improving robustness of object detection models to shadowed conditions
  • Simulating different lighting conditions in synthetic datasets

Mathematical Formulation: For each shadow: 1. A polygon with shadow_dimension vertices is generated within the shadow ROI. 2. The shadow intensity a is randomly chosen from shadow_intensity_range. 3. For each pixel (x, y) within the polygon: new_pixel_value = original_pixel_value * (1 - a)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage","title":"Default usage","text":"Python
>>> transform = A.RandomShadow(p=1.0)\n>>> shadowed_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-shadow-parameters","title":"Custom shadow parameters","text":"Python
>>> transform = A.RandomShadow(\n...     shadow_roi=(0.2, 0.2, 0.8, 0.8),\n...     num_shadows_limit=(2, 4),\n...     shadow_dimension=8,\n...     shadow_intensity_range=(0.3, 0.7),\n...     p=1.0\n... )\n>>> shadowed_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--combining-with-other-transforms","title":"Combining with other transforms","text":"Python
>>> transform = A.Compose([\n...     A.RandomShadow(p=0.5),\n...     A.RandomBrightnessContrast(p=0.5),\n... ])\n>>> augmented_image = transform(image=image)[\"image\"]\n

References

  • Shadow detection and removal: https://www.sciencedirect.com/science/article/pii/S1047320315002035
  • Shadows in computer vision: https://en.wikipedia.org/wiki/Shadow_detection

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomShadow(ImageOnlyTransform):\n    \"\"\"Simulates shadows for the image by reducing the brightness of the image in shadow regions.\n\n    This transform adds realistic shadow effects to images, which can be useful for augmenting\n    datasets for outdoor scene analysis, autonomous driving, or any computer vision task where\n    shadows may be present.\n\n    Args:\n        shadow_roi (tuple[float, float, float, float]): Region of the image where shadows\n            will appear (x_min, y_min, x_max, y_max). All values should be in range [0, 1].\n            Default: (0, 0.5, 1, 1).\n        num_shadows_limit (tuple[int, int]): Lower and upper limits for the possible number of shadows.\n            Default: (1, 2).\n        shadow_dimension (int): Number of edges in the shadow polygons. Default: 5.\n        shadow_intensity_range (tuple[float, float]): Range for the shadow intensity. Larger value\n            means darker shadow. Should be two float values between 0 and 1. Default: (0.5, 0.5).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - Shadows are created by generating random polygons within the specified ROI and\n          reducing the brightness of the image in these areas.\n        - The number of shadows, their shapes, and intensities can be randomized for variety.\n        - This transform is particularly useful for:\n          * Augmenting datasets for outdoor scene understanding\n          * Improving robustness of object detection models to shadowed conditions\n          * Simulating different lighting conditions in synthetic datasets\n\n    Mathematical Formulation:\n        For each shadow:\n        1. A polygon with `shadow_dimension` vertices is generated within the shadow ROI.\n        2. The shadow intensity a is randomly chosen from `shadow_intensity_range`.\n        3. For each pixel (x, y) within the polygon:\n           new_pixel_value = original_pixel_value * (1 - a)\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage\n        >>> transform = A.RandomShadow(p=1.0)\n        >>> shadowed_image = transform(image=image)[\"image\"]\n\n        # Custom shadow parameters\n        >>> transform = A.RandomShadow(\n        ...     shadow_roi=(0.2, 0.2, 0.8, 0.8),\n        ...     num_shadows_limit=(2, 4),\n        ...     shadow_dimension=8,\n        ...     shadow_intensity_range=(0.3, 0.7),\n        ...     p=1.0\n        ... )\n        >>> shadowed_image = transform(image=image)[\"image\"]\n\n        # Combining with other transforms\n        >>> transform = A.Compose([\n        ...     A.RandomShadow(p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.5),\n        ... ])\n        >>> augmented_image = transform(image=image)[\"image\"]\n\n    References:\n        - Shadow detection and removal: https://www.sciencedirect.com/science/article/pii/S1047320315002035\n        - Shadows in computer vision: https://en.wikipedia.org/wiki/Shadow_detection\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        shadow_roi: tuple[float, float, float, float]\n        num_shadows_limit: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n        num_shadows_lower: int | None\n        num_shadows_upper: int | None\n        shadow_dimension: int = Field(ge=3)\n\n        shadow_intensity_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @model_validator(mode=\"after\")\n        def validate_shadows(self) -> Self:\n            if self.num_shadows_lower is not None:\n                warn(\n                    \"`num_shadows_lower` is deprecated. Use `num_shadows_limit` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.num_shadows_upper is not None:\n                warn(\n                    \"`num_shadows_upper` is deprecated. Use `num_shadows_limit` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.num_shadows_lower is not None or self.num_shadows_upper is not None:\n                num_shadows_lower = (\n                    self.num_shadows_lower if self.num_shadows_lower is not None else self.num_shadows_limit[0]\n                )\n                num_shadows_upper = (\n                    self.num_shadows_upper if self.num_shadows_upper is not None else self.num_shadows_limit[1]\n                )\n\n                self.num_shadows_limit = (num_shadows_lower, num_shadows_upper)\n                self.num_shadows_lower = None\n                self.num_shadows_upper = None\n\n            shadow_lower_x, shadow_lower_y, shadow_upper_x, shadow_upper_y = self.shadow_roi\n\n            if not 0 <= shadow_lower_x <= shadow_upper_x <= 1 or not 0 <= shadow_lower_y <= shadow_upper_y <= 1:\n                raise ValueError(f\"Invalid shadow_roi. Got: {self.shadow_roi}\")\n\n            if isinstance(self.shadow_intensity_range, float):\n                if not (0 <= self.shadow_intensity_range <= 1):\n                    raise ValueError(\n                        f\"shadow_intensity_range value should be within [0, 1] range. \"\n                        f\"Got: {self.shadow_intensity_range}\",\n                    )\n            elif isinstance(self.shadow_intensity_range, tuple):\n                if not (0 <= self.shadow_intensity_range[0] <= self.shadow_intensity_range[1] <= 1):\n                    raise ValueError(\n                        f\"shadow_intensity_range values should be within [0, 1] range and increasing. \"\n                        f\"Got: {self.shadow_intensity_range}\",\n                    )\n            else:\n                raise TypeError(\n                    \"shadow_intensity_range should be an float or a tuple of floats.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        shadow_roi: tuple[float, float, float, float] = (0, 0.5, 1, 1),\n        num_shadows_limit: tuple[int, int] = (1, 2),\n        num_shadows_lower: int | None = None,\n        num_shadows_upper: int | None = None,\n        shadow_dimension: int = 5,\n        shadow_intensity_range: tuple[float, float] = (0.5, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.shadow_roi = shadow_roi\n        self.shadow_dimension = shadow_dimension\n        self.num_shadows_limit = num_shadows_limit\n        self.shadow_intensity_range = shadow_intensity_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        vertices_list: list[np.ndarray],\n        intensities: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.add_shadow(img, vertices_list, intensities)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, list[np.ndarray]]:\n        height, width = params[\"shape\"][:2]\n\n        num_shadows = self.py_random.randint(*self.num_shadows_limit)\n\n        x_min, y_min, x_max, y_max = self.shadow_roi\n\n        x_min = int(x_min * width)\n        x_max = int(x_max * width)\n        y_min = int(y_min * height)\n        y_max = int(y_max * height)\n\n        vertices_list = [\n            np.stack(\n                [\n                    self.random_generator.integers(\n                        x_min,\n                        x_max,\n                        size=self.shadow_dimension,\n                    ),\n                    self.random_generator.integers(\n                        y_min,\n                        y_max,\n                        size=self.shadow_dimension,\n                    ),\n                ],\n                axis=1,\n            )\n            for _ in range(num_shadows)\n        ]\n\n        # Sample shadow intensity for each shadow\n        intensities = self.random_generator.uniform(\n            *self.shadow_intensity_range,\n            size=num_shadows,\n        )\n\n        return {\"vertices_list\": vertices_list, \"intensities\": intensities}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"shadow_roi\",\n            \"num_shadows_limit\",\n            \"shadow_dimension\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomSnow","title":"class RandomSnow (snow_point_lower=None, snow_point_upper=None, brightness_coeff=2.5, snow_point_range=(0.1, 0.3), method='bleach', always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies a random snow effect to the input image.

This transform simulates snowfall by either bleaching out some pixel values or adding a snow texture to the image, depending on the chosen method.

Parameters:

Name Type Description snow_point_range tuple[float, float]

Range for the snow point threshold. Both values should be in the (0, 1) range. Default: (0.1, 0.3).

brightness_coeff float

Coefficient applied to increase the brightness of pixels below the snow_point threshold. Larger values lead to more pronounced snow effects. Should be > 0. Default: 2.5.

method Literal[\"bleach\", \"texture\"]

The snow simulation method to use. Options are: - \"bleach\": Uses a simple pixel value thresholding technique. - \"texture\": Applies a more realistic snow texture overlay. Default: \"texture\".

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • The \"bleach\" method increases the brightness of pixels above a certain threshold, creating a simple snow effect. This method is faster but may look less realistic.
  • The \"texture\" method creates a more realistic snow effect through the following steps:
  • Converts the image to HSV color space for better control over brightness.
  • Increases overall image brightness to simulate the reflective nature of snow.
  • Generates a snow texture using Gaussian noise, which is then smoothed with a Gaussian filter.
  • Applies a depth effect to the snow texture, making it more prominent at the top of the image.
  • Blends the snow texture with the original image using alpha compositing.
  • Adds a slight blue tint to simulate the cool color of snow.
  • Adds random sparkle effects to simulate light reflecting off snow crystals. This method produces a more realistic result but is computationally more expensive.

Mathematical Formulation: For the \"bleach\" method: Let L be the lightness channel in HLS color space. For each pixel (i, j): If L[i, j] > snow_point: L[i, j] = L[i, j] * brightness_coeff

For the \"texture\" method:\n1. Brightness adjustment: V_new = V * (1 + brightness_coeff * snow_point)\n2. Snow texture generation: T = GaussianFilter(GaussianNoise(\u03bc=0.5, sigma=0.3))\n3. Depth effect: D = LinearGradient(1.0 to 0.2)\n4. Final pixel value: P = (1 - alpha) * original_pixel + alpha * (T * D * 255)\n   where alpha is the snow intensity factor derived from snow_point.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-usage-bleach-method","title":"Default usage (bleach method)","text":"Python
>>> transform = A.RandomSnow(p=1.0)\n>>> snowy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--using-texture-method-with-custom-parameters","title":"Using texture method with custom parameters","text":"Python
>>> transform = A.RandomSnow(\n...     snow_point_range=(0.2, 0.4),\n...     brightness_coeff=2.0,\n...     method=\"texture\",\n...     p=1.0\n... )\n>>> snowy_image = transform(image=image)[\"image\"]\n

References

  • Bleach method: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library
  • Texture method: Inspired by computer graphics techniques for snow rendering and atmospheric scattering simulations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomSnow(ImageOnlyTransform):\n    \"\"\"Applies a random snow effect to the input image.\n\n    This transform simulates snowfall by either bleaching out some pixel values or\n    adding a snow texture to the image, depending on the chosen method.\n\n    Args:\n        snow_point_range (tuple[float, float]): Range for the snow point threshold.\n            Both values should be in the (0, 1) range. Default: (0.1, 0.3).\n        brightness_coeff (float): Coefficient applied to increase the brightness of pixels\n            below the snow_point threshold. Larger values lead to more pronounced snow effects.\n            Should be > 0. Default: 2.5.\n        method (Literal[\"bleach\", \"texture\"]): The snow simulation method to use. Options are:\n            - \"bleach\": Uses a simple pixel value thresholding technique.\n            - \"texture\": Applies a more realistic snow texture overlay.\n            Default: \"texture\".\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The \"bleach\" method increases the brightness of pixels above a certain threshold,\n          creating a simple snow effect. This method is faster but may look less realistic.\n        - The \"texture\" method creates a more realistic snow effect through the following steps:\n          1. Converts the image to HSV color space for better control over brightness.\n          2. Increases overall image brightness to simulate the reflective nature of snow.\n          3. Generates a snow texture using Gaussian noise, which is then smoothed with a Gaussian filter.\n          4. Applies a depth effect to the snow texture, making it more prominent at the top of the image.\n          5. Blends the snow texture with the original image using alpha compositing.\n          6. Adds a slight blue tint to simulate the cool color of snow.\n          7. Adds random sparkle effects to simulate light reflecting off snow crystals.\n          This method produces a more realistic result but is computationally more expensive.\n\n    Mathematical Formulation:\n        For the \"bleach\" method:\n        Let L be the lightness channel in HLS color space.\n        For each pixel (i, j):\n        If L[i, j] > snow_point:\n            L[i, j] = L[i, j] * brightness_coeff\n\n        For the \"texture\" method:\n        1. Brightness adjustment: V_new = V * (1 + brightness_coeff * snow_point)\n        2. Snow texture generation: T = GaussianFilter(GaussianNoise(\u03bc=0.5, sigma=0.3))\n        3. Depth effect: D = LinearGradient(1.0 to 0.2)\n        4. Final pixel value: P = (1 - alpha) * original_pixel + alpha * (T * D * 255)\n           where alpha is the snow intensity factor derived from snow_point.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Default usage (bleach method)\n        >>> transform = A.RandomSnow(p=1.0)\n        >>> snowy_image = transform(image=image)[\"image\"]\n\n        # Using texture method with custom parameters\n        >>> transform = A.RandomSnow(\n        ...     snow_point_range=(0.2, 0.4),\n        ...     brightness_coeff=2.0,\n        ...     method=\"texture\",\n        ...     p=1.0\n        ... )\n        >>> snowy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Bleach method: https://github.com/UjjwalSaxena/Automold--Road-Augmentation-Library\n        - Texture method: Inspired by computer graphics techniques for snow rendering\n          and atmospheric scattering simulations.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        snow_point_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        snow_point_lower: float | None = Field(\n            gt=0,\n            lt=1,\n        )\n        snow_point_upper: float | None = Field(\n            gt=0,\n            lt=1,\n        )\n        brightness_coeff: float = Field(gt=0)\n        method: Literal[\"bleach\", \"texture\"]\n\n        @model_validator(mode=\"after\")\n        def validate_ranges(self) -> Self:\n            if self.snow_point_lower is not None or self.snow_point_upper is not None:\n                if self.snow_point_lower is not None:\n                    warn(\n                        \"`snow_point_lower` deprecated. Use `snow_point_range` as tuple\"\n                        \" (snow_point_lower, snow_point_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.snow_point_upper is not None:\n                    warn(\n                        \"`snow_point_upper` deprecated. Use `snow_point_range` as tuple\"\n                        \"(snow_point_lower, snow_point_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.snow_point_lower if self.snow_point_lower is not None else self.snow_point_range[0]\n                upper = self.snow_point_upper if self.snow_point_upper is not None else self.snow_point_range[1]\n                self.snow_point_range = (lower, upper)\n                self.snow_point_lower = None\n                self.snow_point_upper = None\n\n            # Validate the snow_point_range\n            if not (0 < self.snow_point_range[0] <= self.snow_point_range[1] < 1):\n                raise ValueError(\n                    \"snow_point_range values should be increasing within (0, 1) range.\",\n                )\n\n            return self\n\n    def __init__(\n        self,\n        snow_point_lower: float | None = None,\n        snow_point_upper: float | None = None,\n        brightness_coeff: float = 2.5,\n        snow_point_range: tuple[float, float] = (0.1, 0.3),\n        method: Literal[\"bleach\", \"texture\"] = \"bleach\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.snow_point_range = snow_point_range\n        self.brightness_coeff = brightness_coeff\n        self.method = method\n\n    def apply(\n        self,\n        img: np.ndarray,\n        snow_point: float,\n        snow_texture: np.ndarray,\n        sparkle_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n\n        if self.method == \"bleach\":\n            return fmain.add_snow_bleach(img, snow_point, self.brightness_coeff)\n        if self.method == \"texture\":\n            return fmain.add_snow_texture(\n                img,\n                snow_point,\n                self.brightness_coeff,\n                snow_texture,\n                sparkle_mask,\n            )\n\n        raise ValueError(f\"Unknown snow method: {self.method}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray | None]:\n        image_shape = params[\"shape\"][:2]\n        result = {\n            \"snow_point\": self.py_random.uniform(*self.snow_point_range),\n            \"snow_texture\": None,\n            \"sparkle_mask\": None,\n        }\n\n        if self.method == \"texture\":\n            snow_texture, sparkle_mask = fmain.generate_snow_textures(\n                img_shape=image_shape,\n                random_generator=self.random_generator,\n            )\n            result[\"snow_texture\"] = snow_texture\n            result[\"sparkle_mask\"] = sparkle_mask\n\n        return result\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"snow_point_range\", \"brightness_coeff\", \"method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomSunFlare","title":"class RandomSunFlare (flare_roi=(0, 0, 1, 0.5), angle_lower=None, angle_upper=None, num_flare_circles_lower=None, num_flare_circles_upper=None, src_radius=400, src_color=(255, 255, 255), angle_range=(0, 1), num_flare_circles_range=(6, 10), method='overlay', always_apply=None, p=0.5) [view source on GitHub]","text":"

Simulates a sun flare effect on the image by adding circles of light.

This transform creates a sun flare effect by overlaying multiple semi-transparent circles of varying sizes and intensities along a line originating from a \"sun\" point. It offers two methods: a simple overlay technique and a more complex physics-based approach.

Parameters:

Name Type Description flare_roi tuple[float, float, float, float]

Region of interest where the sun flare can appear. Values are in the range [0, 1] and represent (x_min, y_min, x_max, y_max) in relative coordinates. Default: (0, 0, 1, 0.5).

angle_range tuple[float, float]

Range of angles (in radians) for the flare direction. Values should be in the range [0, 1], where 0 represents 0 radians and 1 represents 2\u03c0 radians. Default: (0, 1).

num_flare_circles_range tuple[int, int]

Range for the number of flare circles to generate. Default: (6, 10).

src_radius int

Radius of the sun circle in pixels. Default: 400.

src_color tuple[int, int, int]

Color of the sun in RGB format. Default: (255, 255, 255).

method Literal[\"overlay\", \"physics_based\"]

Method to use for generating the sun flare. \"overlay\" uses a simple alpha blending technique, while \"physics_based\" simulates more realistic optical phenomena. Default: \"physics_based\".

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: 3

Note

The transform offers two methods for generating sun flares:

  1. Overlay Method (\"overlay\"):
  2. Creates a simple sun flare effect using basic alpha blending.
  3. Steps: a. Generate the main sun circle with a radial gradient. b. Create smaller flare circles along the flare line. c. Blend these elements with the original image using alpha compositing.
  4. Characteristics:

    • Faster computation
    • Less realistic appearance
    • Suitable for basic augmentation or when performance is a priority
  5. Physics-based Method (\"physics_based\"):

  6. Simulates more realistic optical phenomena observed in actual lens flares.
  7. Steps: a. Create a separate flare layer for complex manipulations. b. Add the main sun circle and diffraction spikes to simulate light diffraction. c. Generate and add multiple flare circles with varying properties. d. Apply Gaussian blur to create a soft, glowing effect. e. Create and apply a radial gradient mask for natural fading from the center. f. Simulate chromatic aberration by applying different blurs to color channels. g. Blend the flare with the original image using screen blending mode.
  8. Characteristics:
    • More computationally intensive
    • Produces more realistic and visually appealing results
    • Includes effects like diffraction spikes and chromatic aberration
    • Suitable for high-quality augmentation or realistic image synthesis

Mathematical Formulation: For both methods: 1. Sun position (x_s, y_s) is randomly chosen within the specified ROI. 2. Flare angle \u03b8 is randomly chosen from the angle_range. 3. For each flare circle i: - Position (x_i, y_i) = (x_s + t_i * cos(\u03b8), y_s + t_i * sin(\u03b8)) where t_i is a random distance along the flare line. - Radius r_i is randomly chosen, with larger circles closer to the sun. - Alpha (transparency) alpha_i is randomly chosen in the range [0.05, 0.2]. - Color (R_i, G_i, B_i) is randomly chosen close to src_color.

Overlay method blending:\nnew_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\nPhysics-based method blending:\nnew_pixel = 255 - ((255 - original_pixel) * (255 - flare_pixel) / 255)\n\n4. Each flare circle is blended with the image using alpha compositing:\n   new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-sun-flare-overlay-method","title":"Default sun flare (overlay method)","text":"Python
>>> transform = A.RandomSunFlare(p=1.0)\n>>> flared_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--physics-based-sun-flare-with-custom-parameters","title":"Physics-based sun flare with custom parameters","text":""},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--default-sun-flare","title":"Default sun flare","text":"Python
>>> transform = A.RandomSunFlare(p=1.0)\n>>> flared_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--custom-sun-flare-parameters","title":"Custom sun flare parameters","text":"Python
>>> transform = A.RandomSunFlare(\n...     flare_roi=(0.1, 0, 0.9, 0.3),\n...     angle_range=(0.25, 0.75),\n...     num_flare_circles_range=(5, 15),\n...     src_radius=200,\n...     src_color=(255, 200, 100),\n...     method=\"physics_based\",\n...     p=1.0\n... )\n>>> flared_image = transform(image=image)[\"image\"]\n

References

  • Lens flare: https://en.wikipedia.org/wiki/Lens_flare
  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Diffraction: https://en.wikipedia.org/wiki/Diffraction
  • Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration
  • Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomSunFlare(ImageOnlyTransform):\n    \"\"\"Simulates a sun flare effect on the image by adding circles of light.\n\n    This transform creates a sun flare effect by overlaying multiple semi-transparent\n    circles of varying sizes and intensities along a line originating from a \"sun\" point.\n    It offers two methods: a simple overlay technique and a more complex physics-based approach.\n\n    Args:\n        flare_roi (tuple[float, float, float, float]): Region of interest where the sun flare\n            can appear. Values are in the range [0, 1] and represent (x_min, y_min, x_max, y_max)\n            in relative coordinates. Default: (0, 0, 1, 0.5).\n        angle_range (tuple[float, float]): Range of angles (in radians) for the flare direction.\n            Values should be in the range [0, 1], where 0 represents 0 radians and 1 represents 2\u03c0 radians.\n            Default: (0, 1).\n        num_flare_circles_range (tuple[int, int]): Range for the number of flare circles to generate.\n            Default: (6, 10).\n        src_radius (int): Radius of the sun circle in pixels. Default: 400.\n        src_color (tuple[int, int, int]): Color of the sun in RGB format. Default: (255, 255, 255).\n        method (Literal[\"overlay\", \"physics_based\"]): Method to use for generating the sun flare.\n            \"overlay\" uses a simple alpha blending technique, while \"physics_based\" simulates\n            more realistic optical phenomena. Default: \"physics_based\".\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        3\n\n    Note:\n        The transform offers two methods for generating sun flares:\n\n        1. Overlay Method (\"overlay\"):\n           - Creates a simple sun flare effect using basic alpha blending.\n           - Steps:\n             a. Generate the main sun circle with a radial gradient.\n             b. Create smaller flare circles along the flare line.\n             c. Blend these elements with the original image using alpha compositing.\n           - Characteristics:\n             * Faster computation\n             * Less realistic appearance\n             * Suitable for basic augmentation or when performance is a priority\n\n        2. Physics-based Method (\"physics_based\"):\n           - Simulates more realistic optical phenomena observed in actual lens flares.\n           - Steps:\n             a. Create a separate flare layer for complex manipulations.\n             b. Add the main sun circle and diffraction spikes to simulate light diffraction.\n             c. Generate and add multiple flare circles with varying properties.\n             d. Apply Gaussian blur to create a soft, glowing effect.\n             e. Create and apply a radial gradient mask for natural fading from the center.\n             f. Simulate chromatic aberration by applying different blurs to color channels.\n             g. Blend the flare with the original image using screen blending mode.\n           - Characteristics:\n             * More computationally intensive\n             * Produces more realistic and visually appealing results\n             * Includes effects like diffraction spikes and chromatic aberration\n             * Suitable for high-quality augmentation or realistic image synthesis\n\n    Mathematical Formulation:\n        For both methods:\n        1. Sun position (x_s, y_s) is randomly chosen within the specified ROI.\n        2. Flare angle \u03b8 is randomly chosen from the angle_range.\n        3. For each flare circle i:\n           - Position (x_i, y_i) = (x_s + t_i * cos(\u03b8), y_s + t_i * sin(\u03b8))\n             where t_i is a random distance along the flare line.\n           - Radius r_i is randomly chosen, with larger circles closer to the sun.\n           - Alpha (transparency) alpha_i is randomly chosen in the range [0.05, 0.2].\n           - Color (R_i, G_i, B_i) is randomly chosen close to src_color.\n\n        Overlay method blending:\n        new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\n        Physics-based method blending:\n        new_pixel = 255 - ((255 - original_pixel) * (255 - flare_pixel) / 255)\n\n        4. Each flare circle is blended with the image using alpha compositing:\n           new_pixel = (1 - alpha_i) * original_pixel + alpha_i * flare_color_i\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [1000, 1000, 3], dtype=np.uint8)\n\n        # Default sun flare (overlay method)\n        >>> transform = A.RandomSunFlare(p=1.0)\n        >>> flared_image = transform(image=image)[\"image\"]\n\n        # Physics-based sun flare with custom parameters\n\n        # Default sun flare\n        >>> transform = A.RandomSunFlare(p=1.0)\n        >>> flared_image = transform(image=image)[\"image\"]\n\n        # Custom sun flare parameters\n\n        >>> transform = A.RandomSunFlare(\n        ...     flare_roi=(0.1, 0, 0.9, 0.3),\n        ...     angle_range=(0.25, 0.75),\n        ...     num_flare_circles_range=(5, 15),\n        ...     src_radius=200,\n        ...     src_color=(255, 200, 100),\n        ...     method=\"physics_based\",\n        ...     p=1.0\n        ... )\n        >>> flared_image = transform(image=image)[\"image\"]\n\n    References:\n        - Lens flare: https://en.wikipedia.org/wiki/Lens_flare\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Diffraction: https://en.wikipedia.org/wiki/Diffraction\n        - Chromatic aberration: https://en.wikipedia.org/wiki/Chromatic_aberration\n        - Screen blending: https://en.wikipedia.org/wiki/Blend_modes#Screen\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        flare_roi: tuple[float, float, float, float]\n        angle_lower: float | None = Field(ge=0, le=1)\n        angle_upper: float | None = Field(ge=0, le=1)\n\n        num_flare_circles_lower: int | None = Field(\n            ge=0,\n        )\n        num_flare_circles_upper: int | None = Field(\n            gt=0,\n        )\n        src_radius: int = Field(gt=1)\n        src_color: tuple[int, ...]\n\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        num_flare_circles_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n        method: Literal[\"overlay\", \"physics_based\"]\n\n        @model_validator(mode=\"after\")\n        def validate_parameters(self) -> Self:\n            (\n                flare_center_lower_x,\n                flare_center_lower_y,\n                flare_center_upper_x,\n                flare_center_upper_y,\n            ) = self.flare_roi\n            if (\n                not 0 <= flare_center_lower_x < flare_center_upper_x <= 1\n                or not 0 <= flare_center_lower_y < flare_center_upper_y <= 1\n            ):\n                raise ValueError(f\"Invalid flare_roi. Got: {self.flare_roi}\")\n\n            if self.angle_lower is not None or self.angle_upper is not None:\n                if self.angle_lower is not None:\n                    warn(\n                        \"`angle_lower` deprecated. Use `angle_range` as tuple (angle_lower, angle_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.angle_upper is not None:\n                    warn(\n                        \"`angle_upper` deprecated. Use `angle_range` as tuple(angle_lower, angle_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = self.angle_lower if self.angle_lower is not None else self.angle_range[0]\n                upper = self.angle_upper if self.angle_upper is not None else self.angle_range[1]\n                self.angle_range = (lower, upper)\n\n            if self.num_flare_circles_lower is not None or self.num_flare_circles_upper is not None:\n                if self.num_flare_circles_lower is not None:\n                    warn(\n                        \"`num_flare_circles_lower` deprecated. Use `num_flare_circles_range` as tuple\"\n                        \" (num_flare_circles_lower, num_flare_circles_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                if self.num_flare_circles_upper is not None:\n                    warn(\n                        \"`num_flare_circles_upper` deprecated. Use `num_flare_circles_range` as tuple\"\n                        \" (num_flare_circles_lower, num_flare_circles_upper) instead.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                lower = (\n                    self.num_flare_circles_lower\n                    if self.num_flare_circles_lower is not None\n                    else self.num_flare_circles_range[0]\n                )\n                upper = (\n                    self.num_flare_circles_upper\n                    if self.num_flare_circles_upper is not None\n                    else self.num_flare_circles_range[1]\n                )\n                self.num_flare_circles_range = (lower, upper)\n\n            return self\n\n    def __init__(\n        self,\n        flare_roi: tuple[float, float, float, float] = (0, 0, 1, 0.5),\n        angle_lower: float | None = None,\n        angle_upper: float | None = None,\n        num_flare_circles_lower: int | None = None,\n        num_flare_circles_upper: int | None = None,\n        src_radius: int = 400,\n        src_color: tuple[int, ...] = (255, 255, 255),\n        angle_range: tuple[float, float] = (0, 1),\n        num_flare_circles_range: tuple[int, int] = (6, 10),\n        method: Literal[\"overlay\", \"physics_based\"] = \"overlay\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.angle_range = angle_range\n        self.num_flare_circles_range = num_flare_circles_range\n\n        self.src_radius = src_radius\n        self.src_color = src_color\n        self.flare_roi = flare_roi\n        self.method = method\n\n    def apply(\n        self,\n        img: np.ndarray,\n        flare_center: tuple[float, float],\n        circles: list[Any],\n        **params: Any,\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        if self.method == \"overlay\":\n            return fmain.add_sun_flare_overlay(\n                img,\n                flare_center,\n                self.src_radius,\n                self.src_color,\n                circles,\n            )\n        if self.method == \"physics_based\":\n            return fmain.add_sun_flare_physics_based(\n                img,\n                flare_center,\n                self.src_radius,\n                self.src_color,\n                circles,\n            )\n\n        raise ValueError(f\"Invalid method: {self.method}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        diagonal = math.sqrt(height**2 + width**2)\n\n        angle = 2 * math.pi * self.py_random.uniform(*self.angle_range)\n\n        # Calculate flare center in pixel coordinates\n        x_min, y_min, x_max, y_max = self.flare_roi\n        flare_center_x = int(width * self.py_random.uniform(x_min, x_max))\n        flare_center_y = int(height * self.py_random.uniform(y_min, y_max))\n\n        num_circles = self.py_random.randint(*self.num_flare_circles_range)\n\n        # Calculate parameters relative to image size\n        step_size = max(1, int(diagonal * 0.01))  # 1% of diagonal, minimum 1 pixel\n        max_radius = max(2, int(height * 0.01))  # 1% of height, minimum 2 pixels\n        color_range = int(max(self.src_color) * 0.2)  # 20% of max color value\n\n        def line(t: float) -> tuple[float, float]:\n            return (\n                flare_center_x + t * math.cos(angle),\n                flare_center_y + t * math.sin(angle),\n            )\n\n        # Generate points along the flare line\n        t_range = range(-flare_center_x, width - flare_center_x, step_size)\n        points = [line(t) for t in t_range]\n\n        circles = []\n        for _ in range(num_circles):\n            alpha = self.py_random.uniform(0.05, 0.2)\n            point = self.py_random.choice(points)\n            rad = self.py_random.randint(1, max_radius)\n\n            # Generate colors relative to src_color\n            colors = [self.py_random.randint(max(c - color_range, 0), c) for c in self.src_color]\n\n            circles.append(\n                (\n                    alpha,\n                    (int(point[0]), int(point[1])),\n                    pow(rad, 3),\n                    tuple(colors),\n                ),\n            )\n\n        return {\n            \"circles\": circles,\n            \"flare_center\": (flare_center_x, flare_center_y),\n        }\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"flare_roi\": self.flare_roi,\n            \"angle_range\": self.angle_range,\n            \"num_flare_circles_range\": self.num_flare_circles_range,\n            \"src_radius\": self.src_radius,\n            \"src_color\": self.src_color,\n        }\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RandomToneCurve","title":"class RandomToneCurve (scale=0.1, per_channel=False, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly change the relationship between bright and dark areas of the image by manipulating its tone curve.

This transform applies a random S-curve to the image's tone curve, adjusting the brightness and contrast in a non-linear manner. It can be applied to the entire image or to each channel separately.

Parameters:

Name Type Description scale float

Standard deviation of the normal distribution used to sample random distances to move two control points that modify the image's curve. Values should be in range [0, 1]. Higher values will result in more dramatic changes to the image. Default: 0.1

per_channel bool

If True, the tone curve will be applied to each channel of the input image separately, which can lead to color distortion. If False, the same curve is applied to all channels, preserving the original color relationships. Default: False

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform modifies the image's histogram by applying a smooth, S-shaped curve to it.
  • The S-curve is defined by moving two control points of a quadratic B\u00e9zier curve.
  • When per_channel is False, the same curve is applied to all channels, maintaining color balance.
  • When per_channel is True, different curves are applied to each channel, which can create color shifts.
  • This transform can be used to adjust image contrast and brightness in a more natural way than linear transforms.
  • The effect can range from subtle contrast adjustments to more dramatic \"vintage\" or \"faded\" looks.

Mathematical Formulation: 1. Two control points are randomly moved from their default positions (0.25, 0.25) and (0.75, 0.75). 2. The new positions are sampled from a normal distribution: N(\u03bc, \u03c3\u00b2), where \u03bc is the original position and alpha is the scale parameter. 3. These points, along with fixed points at (0, 0) and (1, 1), define a quadratic B\u00e9zier curve. 4. The curve is applied as a lookup table to the image intensities: new_intensity = curve(original_intensity)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-a-random-tone-curve-to-all-channels-together","title":"Apply a random tone curve to all channels together","text":"Python
>>> transform = A.RandomToneCurve(scale=0.1, per_channel=False, p=1.0)\n>>> augmented_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-random-tone-curves-to-each-channel-separately","title":"Apply random tone curves to each channel separately","text":"Python
>>> transform = A.RandomToneCurve(scale=0.2, per_channel=True, p=1.0)\n>>> augmented_image = transform(image=image)['image']\n

References

  • \"What Else Can Fool Deep Learning? Addressing Color Constancy Errors on Deep Neural Network Performance\" by Mahmoud Afifi and Michael S. Brown, ICCV 2019.
  • B\u00e9zier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B%C3%A9zier_curves
  • Tone mapping: https://en.wikipedia.org/wiki/Tone_mapping

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RandomToneCurve(ImageOnlyTransform):\n    \"\"\"Randomly change the relationship between bright and dark areas of the image by manipulating its tone curve.\n\n    This transform applies a random S-curve to the image's tone curve, adjusting the brightness and contrast\n    in a non-linear manner. It can be applied to the entire image or to each channel separately.\n\n    Args:\n        scale (float): Standard deviation of the normal distribution used to sample random distances\n            to move two control points that modify the image's curve. Values should be in range [0, 1].\n            Higher values will result in more dramatic changes to the image. Default: 0.1\n        per_channel (bool): If True, the tone curve will be applied to each channel of the input image separately,\n            which can lead to color distortion. If False, the same curve is applied to all channels,\n            preserving the original color relationships. Default: False\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform modifies the image's histogram by applying a smooth, S-shaped curve to it.\n        - The S-curve is defined by moving two control points of a quadratic B\u00e9zier curve.\n        - When per_channel is False, the same curve is applied to all channels, maintaining color balance.\n        - When per_channel is True, different curves are applied to each channel, which can create color shifts.\n        - This transform can be used to adjust image contrast and brightness in a more natural way than linear\n            transforms.\n        - The effect can range from subtle contrast adjustments to more dramatic \"vintage\" or \"faded\" looks.\n\n    Mathematical Formulation:\n        1. Two control points are randomly moved from their default positions (0.25, 0.25) and (0.75, 0.75).\n        2. The new positions are sampled from a normal distribution: N(\u03bc, \u03c3\u00b2), where \u03bc is the original position\n        and alpha is the scale parameter.\n        3. These points, along with fixed points at (0, 0) and (1, 1), define a quadratic B\u00e9zier curve.\n        4. The curve is applied as a lookup table to the image intensities:\n           new_intensity = curve(original_intensity)\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply a random tone curve to all channels together\n        >>> transform = A.RandomToneCurve(scale=0.1, per_channel=False, p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n        # Apply random tone curves to each channel separately\n        >>> transform = A.RandomToneCurve(scale=0.2, per_channel=True, p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n    References:\n        - \"What Else Can Fool Deep Learning? Addressing Color Constancy Errors on Deep Neural Network Performance\"\n          by Mahmoud Afifi and Michael S. Brown, ICCV 2019.\n        - B\u00e9zier curve: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B%C3%A9zier_curves\n        - Tone mapping: https://en.wikipedia.org/wiki/Tone_mapping\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: float = Field(\n            ge=0,\n            le=1,\n        )\n        per_channel: bool\n\n    def __init__(\n        self,\n        scale: float = 0.1,\n        per_channel: bool = False,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale = scale\n        self.per_channel = per_channel\n\n    def apply(\n        self,\n        img: np.ndarray,\n        low_y: float | np.ndarray,\n        high_y: float | np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.move_tone_curve(img, low_y, high_y)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if self.per_channel and num_channels != 1:\n            return {\n                \"low_y\": np.clip(\n                    self.random_generator.normal(\n                        loc=0.25,\n                        scale=self.scale,\n                        size=(num_channels,),\n                    ),\n                    0,\n                    1,\n                ),\n                \"high_y\": np.clip(\n                    self.random_generator.normal(\n                        loc=0.75,\n                        scale=self.scale,\n                        size=(num_channels,),\n                    ),\n                    0,\n                    1,\n                ),\n            }\n        # Same values for all channels\n        low_y = np.clip(self.random_generator.normal(loc=0.25, scale=self.scale), 0, 1)\n        high_y = np.clip(self.random_generator.normal(loc=0.75, scale=self.scale), 0, 1)\n\n        return {\"low_y\": low_y, \"high_y\": high_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"scale\", \"per_channel\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.RingingOvershoot","title":"class RingingOvershoot (blur_limit=(7, 15), cutoff=(0.7853981633974483, 1.5707963267948966), p=0.5, always_apply=None) [view source on GitHub]","text":"

Create ringing or overshoot artifacts by convolving the image with a 2D sinc filter.

This transform simulates the ringing artifacts that can occur in digital image processing, particularly after sharpening or edge enhancement operations. It creates oscillations or overshoots near sharp transitions in the image.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Maximum kernel size for the sinc filter. Must be an odd number in the range [3, inf). If a single int is provided, the kernel size will be randomly chosen from the range (3, blur_limit). If a tuple (min, max) is provided, the kernel size will be randomly chosen from the range (min, max). Default: (7, 15).

cutoff tuple[float, float]

Range to choose the cutoff frequency in radians. Values should be in the range (0, \u03c0). A lower cutoff frequency will result in more pronounced ringing effects. Default: (\u03c0/4, \u03c0/2).

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • Ringing artifacts are oscillations of the image intensity function in the neighborhood of sharp transitions, such as edges or object boundaries.
  • This transform uses a 2D sinc filter (also known as a 2D cardinal sine function) to introduce these artifacts.
  • The severity of the ringing effect is controlled by both the kernel size (blur_limit) and the cutoff frequency.
  • Larger kernel sizes and lower cutoff frequencies will generally produce more noticeable ringing effects.
  • This transform can be useful for:
  • Simulating imperfections in image processing or transmission systems
  • Testing the robustness of computer vision models to ringing artifacts
  • Creating artistic effects that emphasize edges and transitions in images

Mathematical Formulation: The 2D sinc filter kernel is defined as:

K(x, y) = cutoff * J\u2081(cutoff * \u221a(x\u00b2 + y\u00b2)) / (2\u03c0 * \u221a(x\u00b2 + y\u00b2))\n\nwhere:\n- J\u2081 is the Bessel function of the first kind of order 1\n- cutoff is the chosen cutoff frequency\n- x and y are the distances from the kernel center\n\nThe filtered image I' is obtained by convolving the input image I with the kernel K:\n\nI'(x, y) = \u2211\u2211 I(x-u, y-v) * K(u, v)\n\nThe convolution operation introduces the ringing artifacts near sharp transitions.\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-ringing-effect-with-default-parameters","title":"Apply ringing effect with default parameters","text":"Python
>>> transform = A.RingingOvershoot(p=1.0)\n>>> ringing_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-ringing-effect-with-custom-parameters","title":"Apply ringing effect with custom parameters","text":"Python
>>> transform = A.RingingOvershoot(\n...     blur_limit=(9, 17),\n...     cutoff=(np.pi/6, np.pi/3),\n...     p=1.0\n... )\n>>> ringing_image = transform(image=image)['image']\n

References

  • Ringing artifacts: https://en.wikipedia.org/wiki/Ringing_artifacts
  • Sinc filter: https://en.wikipedia.org/wiki/Sinc_filter
  • \"The Importance of Ringing Artifacts in Image Processing\" by Jae S. Lim, 1981
  • \"Digital Image Processing\" by Rafael C. Gonzalez and Richard E. Woods, 4th Edition

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class RingingOvershoot(ImageOnlyTransform):\n    \"\"\"Create ringing or overshoot artifacts by convolving the image with a 2D sinc filter.\n\n    This transform simulates the ringing artifacts that can occur in digital image processing,\n    particularly after sharpening or edge enhancement operations. It creates oscillations\n    or overshoots near sharp transitions in the image.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Maximum kernel size for the sinc filter.\n            Must be an odd number in the range [3, inf).\n            If a single int is provided, the kernel size will be randomly chosen\n            from the range (3, blur_limit). If a tuple (min, max) is provided,\n            the kernel size will be randomly chosen from the range (min, max).\n            Default: (7, 15).\n        cutoff (tuple[float, float]): Range to choose the cutoff frequency in radians.\n            Values should be in the range (0, \u03c0). A lower cutoff frequency will\n            result in more pronounced ringing effects.\n            Default: (\u03c0/4, \u03c0/2).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - Ringing artifacts are oscillations of the image intensity function in the neighborhood\n          of sharp transitions, such as edges or object boundaries.\n        - This transform uses a 2D sinc filter (also known as a 2D cardinal sine function)\n          to introduce these artifacts.\n        - The severity of the ringing effect is controlled by both the kernel size (blur_limit)\n          and the cutoff frequency.\n        - Larger kernel sizes and lower cutoff frequencies will generally produce more\n          noticeable ringing effects.\n        - This transform can be useful for:\n          * Simulating imperfections in image processing or transmission systems\n          * Testing the robustness of computer vision models to ringing artifacts\n          * Creating artistic effects that emphasize edges and transitions in images\n\n    Mathematical Formulation:\n        The 2D sinc filter kernel is defined as:\n\n        K(x, y) = cutoff * J\u2081(cutoff * \u221a(x\u00b2 + y\u00b2)) / (2\u03c0 * \u221a(x\u00b2 + y\u00b2))\n\n        where:\n        - J\u2081 is the Bessel function of the first kind of order 1\n        - cutoff is the chosen cutoff frequency\n        - x and y are the distances from the kernel center\n\n        The filtered image I' is obtained by convolving the input image I with the kernel K:\n\n        I'(x, y) = \u2211\u2211 I(x-u, y-v) * K(u, v)\n\n        The convolution operation introduces the ringing artifacts near sharp transitions.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n\n        # Apply ringing effect with default parameters\n        >>> transform = A.RingingOvershoot(p=1.0)\n        >>> ringing_image = transform(image=image)['image']\n\n        # Apply ringing effect with custom parameters\n        >>> transform = A.RingingOvershoot(\n        ...     blur_limit=(9, 17),\n        ...     cutoff=(np.pi/6, np.pi/3),\n        ...     p=1.0\n        ... )\n        >>> ringing_image = transform(image=image)['image']\n\n    References:\n        - Ringing artifacts: https://en.wikipedia.org/wiki/Ringing_artifacts\n        - Sinc filter: https://en.wikipedia.org/wiki/Sinc_filter\n        - \"The Importance of Ringing Artifacts in Image Processing\" by Jae S. Lim, 1981\n        - \"Digital Image Processing\" by Rafael C. Gonzalez and Richard E. Woods, 4th Edition\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        blur_limit: ScaleIntType\n        cutoff: Annotated[tuple[float, float], nondecreasing]\n\n        @field_validator(\"cutoff\")\n        @classmethod\n        def check_cutoff(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 0, np.pi\n            check_range(v, *bounds, info.field_name)\n            return v\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (7, 15),\n        cutoff: tuple[float, float] = (np.pi / 4, np.pi / 2),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.cutoff = cutoff\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        ksize = self.py_random.randrange(self.blur_limit[0], self.blur_limit[1] + 1, 2)\n        if ksize % 2 == 0:\n            raise ValueError(f\"Kernel size must be odd. Got: {ksize}\")\n\n        cutoff = self.py_random.uniform(*self.cutoff)\n\n        # From dsp.stackexchange.com/questions/58301/2-d-circularly-symmetric-low-pass-filter\n        with np.errstate(divide=\"ignore\", invalid=\"ignore\"):\n            kernel = np.fromfunction(\n                lambda x, y: cutoff\n                * special.j1(\n                    cutoff * np.sqrt((x - (ksize - 1) / 2) ** 2 + (y - (ksize - 1) / 2) ** 2),\n                )\n                / (2 * np.pi * np.sqrt((x - (ksize - 1) / 2) ** 2 + (y - (ksize - 1) / 2) ** 2)),\n                [ksize, ksize],\n            )\n        kernel[(ksize - 1) // 2, (ksize - 1) // 2] = cutoff**2 / (4 * np.pi)\n\n        # Normalize kernel\n        kernel = kernel.astype(np.float32) / np.sum(kernel)\n\n        return {\"kernel\": kernel}\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel)\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"blur_limit\", \"cutoff\")\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.SaltAndPepper","title":"class SaltAndPepper (amount=(0.01, 0.06), salt_vs_pepper=(0.4, 0.6), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply salt and pepper noise to the input image.

Salt and pepper noise is a form of impulse noise that randomly sets pixels to either maximum value (salt) or minimum value (pepper). The amount and proportion of salt vs pepper noise can be controlled.

Parameters:

Name Type Description amount float, float

Range for total amount of noise (both salt and pepper). Values between 0 and 1. For example: - 0.05 means 5% of all pixels will be replaced with noise - (0.01, 0.06) will sample amount uniformly from 1% to 6% Default: (0.01, 0.06)

salt_vs_pepper float, float

Range for ratio of salt (white) vs pepper (black) noise. Values between 0 and 1. For example: - 0.5 means equal amounts of salt and pepper - 0.7 means 70% of noisy pixels will be salt, 30% pepper - (0.4, 0.6) will sample ratio uniformly from 40% to 60% Default: (0.4, 0.6)

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Note

  • Salt noise sets pixels to maximum value (255 for uint8, 1.0 for float32)
  • Pepper noise sets pixels to 0
  • Salt and pepper masks are generated independently, so a pixel could theoretically be selected for both (in this case, pepper overrides salt)
  • The actual number of affected pixels might slightly differ from the specified amount due to random sampling and potential overlap of salt and pepper masks

Mathematical Formulation: For an input image I, the output O is: O[x,y] = max_value, if salt_mask[x,y] = True O[x,y] = 0, if pepper_mask[x,y] = True O[x,y] = I[x,y], otherwise

where:\nP(salt_mask[x,y] = True) = amount * salt_ratio\nP(pepper_mask[x,y] = True) = amount * (1 - salt_ratio)\namount \u2208 [amount_min, amount_max]\nsalt_ratio \u2208 [salt_vs_pepper_min, salt_vs_pepper_max]\n

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-salt-and-pepper-noise-with-default-parameters","title":"Apply salt and pepper noise with default parameters","text":"Python
>>> transform = A.SaltAndPepper(p=1.0)\n>>> noisy_image = transform(image=image)[\"image\"]\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--heavy-noise-with-more-salt-than-pepper","title":"Heavy noise with more salt than pepper","text":"Python
>>> transform = A.SaltAndPepper(\n...     amount=(0.1, 0.2),       # 10-20% of pixels will be noisy\n...     salt_vs_pepper=(0.7, 0.9),  # 70-90% of noise will be salt\n...     p=1.0\n... )\n>>> noisy_image = transform(image=image)[\"image\"]\n

References

.. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\" Chapter 5: Image Restoration and Reconstruction.

.. [2] A. K. Jain, \"Fundamentals of Digital Image Processing,\" Chapter 7: Image Degradation and Restoration.

.. [3] Salt and pepper noise: https://en.wikipedia.org/wiki/Salt-and-pepper_noise

See Also: - GaussNoise: For additive Gaussian noise - MultiplicativeNoise: For multiplicative noise - ISONoise: For camera sensor noise simulation

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class SaltAndPepper(ImageOnlyTransform):\n    \"\"\"Apply salt and pepper noise to the input image.\n\n    Salt and pepper noise is a form of impulse noise that randomly sets pixels to either maximum value (salt)\n    or minimum value (pepper). The amount and proportion of salt vs pepper noise can be controlled.\n\n    Args:\n        amount ((float, float)): Range for total amount of noise (both salt and pepper).\n            Values between 0 and 1. For example:\n            - 0.05 means 5% of all pixels will be replaced with noise\n            - (0.01, 0.06) will sample amount uniformly from 1% to 6%\n            Default: (0.01, 0.06)\n\n        salt_vs_pepper ((float, float)): Range for ratio of salt (white) vs pepper (black) noise.\n            Values between 0 and 1. For example:\n            - 0.5 means equal amounts of salt and pepper\n            - 0.7 means 70% of noisy pixels will be salt, 30% pepper\n            - (0.4, 0.6) will sample ratio uniformly from 40% to 60%\n            Default: (0.4, 0.6)\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Salt noise sets pixels to maximum value (255 for uint8, 1.0 for float32)\n        - Pepper noise sets pixels to 0\n        - Salt and pepper masks are generated independently, so a pixel could theoretically\n          be selected for both (in this case, pepper overrides salt)\n        - The actual number of affected pixels might slightly differ from the specified amount\n          due to random sampling and potential overlap of salt and pepper masks\n\n    Mathematical Formulation:\n        For an input image I, the output O is:\n        O[x,y] = max_value,  if salt_mask[x,y] = True\n        O[x,y] = 0,         if pepper_mask[x,y] = True\n        O[x,y] = I[x,y],    otherwise\n\n        where:\n        P(salt_mask[x,y] = True) = amount * salt_ratio\n        P(pepper_mask[x,y] = True) = amount * (1 - salt_ratio)\n        amount \u2208 [amount_min, amount_max]\n        salt_ratio \u2208 [salt_vs_pepper_min, salt_vs_pepper_max]\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Apply salt and pepper noise with default parameters\n        >>> transform = A.SaltAndPepper(p=1.0)\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n        # Heavy noise with more salt than pepper\n        >>> transform = A.SaltAndPepper(\n        ...     amount=(0.1, 0.2),       # 10-20% of pixels will be noisy\n        ...     salt_vs_pepper=(0.7, 0.9),  # 70-90% of noise will be salt\n        ...     p=1.0\n        ... )\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n    References:\n        .. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\"\n               Chapter 5: Image Restoration and Reconstruction.\n\n        .. [2] A. K. Jain, \"Fundamentals of Digital Image Processing,\"\n               Chapter 7: Image Degradation and Restoration.\n\n        .. [3] Salt and pepper noise:\n               https://en.wikipedia.org/wiki/Salt-and-pepper_noise\n\n    See Also:\n        - GaussNoise: For additive Gaussian noise\n        - MultiplicativeNoise: For multiplicative noise\n        - ISONoise: For camera sensor noise simulation\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        amount: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        salt_vs_pepper: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n\n    def __init__(\n        self,\n        amount: tuple[float, float] = (0.01, 0.06),\n        salt_vs_pepper: tuple[float, float] = (0.4, 0.6),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.amount = amount\n        self.salt_vs_pepper = salt_vs_pepper\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        # Sample total amount and salt ratio\n        total_amount = self.py_random.uniform(*self.amount)\n        salt_ratio = self.py_random.uniform(*self.salt_vs_pepper)\n\n        # Calculate individual probabilities\n        prob_salt = total_amount * salt_ratio\n        prob_pepper = total_amount * (1 - salt_ratio)\n\n        # Generate masks\n        salt_mask = self.random_generator.random(image.shape) < prob_salt\n        pepper_mask = self.random_generator.random(image.shape) < prob_pepper\n\n        return {\n            \"salt_mask\": salt_mask,\n            \"pepper_mask\": pepper_mask,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        salt_mask: np.ndarray,\n        pepper_mask: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.apply_salt_and_pepper(img, salt_mask, pepper_mask)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"amount\", \"salt_vs_pepper\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Sharpen","title":"class Sharpen (alpha=(0.2, 0.5), lightness=(0.5, 1.0), method='kernel', kernel_size=5, sigma=1.0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Sharpen the input image using either kernel-based or Gaussian interpolation method.

Implements two different approaches to image sharpening: 1. Traditional kernel-based method using Laplacian operator 2. Gaussian interpolation method (similar to Kornia's approach)

Parameters:

Name Type Description alpha tuple[float, float]

Range for the visibility of sharpening effect. At 0, only the original image is visible, at 1.0 only its processed version is visible. Values should be in the range [0, 1]. Used in both methods. Default: (0.2, 0.5).

lightness tuple[float, float]

Range for the lightness of the sharpened image. Only used in 'kernel' method. Larger values create higher contrast. Values should be greater than 0. Default: (0.5, 1.0).

method Literal['kernel', 'gaussian']

Sharpening algorithm to use: - 'kernel': Traditional kernel-based sharpening using Laplacian operator - 'gaussian': Interpolation between Gaussian blurred and original image Default: 'kernel'

kernel_size int

Size of the Gaussian blur kernel for 'gaussian' method. Must be odd. Default: 5

sigma float

Standard deviation for Gaussian kernel in 'gaussian' method. Default: 1.0

p float

Probability of applying the transform. Default: 0.5.

Image types: uint8, float32

Number of channels: Any

Mathematical Formulation: 1. Kernel Method: The sharpening operation is based on the Laplacian operator L: L = [[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]

   The final kernel K is a weighted sum:\n   K = (1 - a)I + a(L + \u03bbI)\n\n   where:\n   - a is the alpha value\n   - \u03bb is the lightness value\n   - I is the identity kernel\n\n   The output image O is computed as:\n   O = K * I  (convolution)\n\n2. Gaussian Method:\n   Based on the unsharp mask principle:\n   O = aI + (1-a)G\n\n   where:\n   - I is the input image\n   - G is the Gaussian blurred version of I\n   - a is the alpha value (sharpness)\n\n   The Gaussian kernel G(x,y) is defined as:\n   G(x,y) = (1/(2\u03c0s\u00b2))exp(-(x\u00b2+y\u00b2)/(2s\u00b2))\n

Note

  • Kernel sizes must be odd to maintain spatial alignment
  • Methods produce different visual results:
  • Kernel method: More pronounced edges, possible artifacts
  • Gaussian method: More natural look, limited to original sharpness

Examples:

Python
>>> import albumentations as A\n>>> import numpy as np\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--traditional-kernel-sharpening","title":"Traditional kernel sharpening","text":"Python
>>> transform = A.Sharpen(\n...     alpha=(0.2, 0.5),\n...     lightness=(0.5, 1.0),\n...     method='kernel',\n...     p=1.0\n... )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--gaussian-interpolation-sharpening","title":"Gaussian interpolation sharpening","text":"Python
>>> transform = A.Sharpen(\n...     alpha=(0.5, 1.0),\n...     method='gaussian',\n...     kernel_size=5,\n...     sigma=1.0,\n...     p=1.0\n... )\n

References

.. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\" Chapter 3: Intensity Transformations and Spatial Filtering.

.. [2] J. C. Russ, \"The Image Processing Handbook (7th Edition),\" Chapter 4: Image Enhancement.

.. [3] T. Acharya and A. K. Ray, \"Image Processing: Principles and Applications,\" Chapter 5: Image Enhancement.

.. [4] Unsharp masking: https://en.wikipedia.org/wiki/Unsharp_masking

.. [5] Laplacian operator: https://en.wikipedia.org/wiki/Laplace_operator

.. [6] Gaussian blur: https://en.wikipedia.org/wiki/Gaussian_blur

See Also: - Blur: For Gaussian blurring - UnsharpMask: Alternative sharpening method - RandomBrightnessContrast: For adjusting image contrast

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Sharpen(ImageOnlyTransform):\n    \"\"\"Sharpen the input image using either kernel-based or Gaussian interpolation method.\n\n    Implements two different approaches to image sharpening:\n    1. Traditional kernel-based method using Laplacian operator\n    2. Gaussian interpolation method (similar to Kornia's approach)\n\n    Args:\n        alpha (tuple[float, float]): Range for the visibility of sharpening effect.\n            At 0, only the original image is visible, at 1.0 only its processed version is visible.\n            Values should be in the range [0, 1].\n            Used in both methods. Default: (0.2, 0.5).\n\n        lightness (tuple[float, float]): Range for the lightness of the sharpened image.\n            Only used in 'kernel' method. Larger values create higher contrast.\n            Values should be greater than 0. Default: (0.5, 1.0).\n\n        method (Literal['kernel', 'gaussian']): Sharpening algorithm to use:\n            - 'kernel': Traditional kernel-based sharpening using Laplacian operator\n            - 'gaussian': Interpolation between Gaussian blurred and original image\n            Default: 'kernel'\n\n        kernel_size (int): Size of the Gaussian blur kernel for 'gaussian' method.\n            Must be odd. Default: 5\n\n        sigma (float): Standard deviation for Gaussian kernel in 'gaussian' method.\n            Default: 1.0\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Mathematical Formulation:\n        1. Kernel Method:\n           The sharpening operation is based on the Laplacian operator L:\n           L = [[-1, -1, -1],\n                [-1,  8, -1],\n                [-1, -1, -1]]\n\n           The final kernel K is a weighted sum:\n           K = (1 - a)I + a(L + \u03bbI)\n\n           where:\n           - a is the alpha value\n           - \u03bb is the lightness value\n           - I is the identity kernel\n\n           The output image O is computed as:\n           O = K * I  (convolution)\n\n        2. Gaussian Method:\n           Based on the unsharp mask principle:\n           O = aI + (1-a)G\n\n           where:\n           - I is the input image\n           - G is the Gaussian blurred version of I\n           - a is the alpha value (sharpness)\n\n           The Gaussian kernel G(x,y) is defined as:\n           G(x,y) = (1/(2\u03c0s\u00b2))exp(-(x\u00b2+y\u00b2)/(2s\u00b2))\n\n    Note:\n        - Kernel sizes must be odd to maintain spatial alignment\n        - Methods produce different visual results:\n          * Kernel method: More pronounced edges, possible artifacts\n          * Gaussian method: More natural look, limited to original sharpness\n\n    Examples:\n        >>> import albumentations as A\n        >>> import numpy as np\n\n        # Traditional kernel sharpening\n        >>> transform = A.Sharpen(\n        ...     alpha=(0.2, 0.5),\n        ...     lightness=(0.5, 1.0),\n        ...     method='kernel',\n        ...     p=1.0\n        ... )\n\n        # Gaussian interpolation sharpening\n        >>> transform = A.Sharpen(\n        ...     alpha=(0.5, 1.0),\n        ...     method='gaussian',\n        ...     kernel_size=5,\n        ...     sigma=1.0,\n        ...     p=1.0\n        ... )\n\n    References:\n        .. [1] R. C. Gonzalez and R. E. Woods, \"Digital Image Processing (4th Edition),\"\n               Chapter 3: Intensity Transformations and Spatial Filtering.\n\n        .. [2] J. C. Russ, \"The Image Processing Handbook (7th Edition),\"\n               Chapter 4: Image Enhancement.\n\n        .. [3] T. Acharya and A. K. Ray, \"Image Processing: Principles and Applications,\"\n               Chapter 5: Image Enhancement.\n\n        .. [4] Unsharp masking:\n               https://en.wikipedia.org/wiki/Unsharp_masking\n\n        .. [5] Laplacian operator:\n               https://en.wikipedia.org/wiki/Laplace_operator\n\n        .. [6] Gaussian blur:\n               https://en.wikipedia.org/wiki/Gaussian_blur\n\n    See Also:\n        - Blur: For Gaussian blurring\n        - UnsharpMask: Alternative sharpening method\n        - RandomBrightnessContrast: For adjusting image contrast\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        alpha: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        lightness: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, None))]\n        method: Literal[\"kernel\", \"gaussian\"]\n        kernel_size: int = Field(ge=3)\n        sigma: float = Field(gt=0)\n\n    @field_validator(\"kernel_size\")\n    @classmethod\n    def check_kernel_size(cls, value: int) -> int:\n        return value + 1 if value % 2 == 0 else value\n\n    def __init__(\n        self,\n        alpha: tuple[float, float] = (0.2, 0.5),\n        lightness: tuple[float, float] = (0.5, 1.0),\n        method: Literal[\"kernel\", \"gaussian\"] = \"kernel\",\n        kernel_size: int = 5,\n        sigma: float = 1.0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.alpha = alpha\n        self.lightness = lightness\n        self.method = method\n        self.kernel_size = kernel_size\n        self.sigma = sigma\n\n    @staticmethod\n    def __generate_sharpening_matrix(\n        alpha: np.ndarray,\n        lightness: np.ndarray,\n    ) -> np.ndarray:\n        matrix_nochange = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=np.float32)\n        matrix_effect = np.array(\n            [[-1, -1, -1], [-1, 8 + lightness, -1], [-1, -1, -1]],\n            dtype=np.float32,\n        )\n\n        return (1 - alpha) * matrix_nochange + alpha * matrix_effect\n\n    def get_params(self) -> dict[str, Any]:\n        alpha = self.py_random.uniform(*self.alpha)\n\n        if self.method == \"kernel\":\n            lightness = self.py_random.uniform(*self.lightness)\n            return {\n                \"alpha\": alpha,\n                \"sharpening_matrix\": self.__generate_sharpening_matrix(\n                    alpha,\n                    lightness,\n                ),\n            }\n\n        return {\"alpha\": alpha, \"sharpening_matrix\": None}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        alpha: float,\n        sharpening_matrix: np.ndarray | None,\n        **params: Any,\n    ) -> np.ndarray:\n        if self.method == \"kernel\":\n            return fmain.convolve(img, sharpening_matrix)\n        return fmain.sharpen_gaussian(img, alpha, self.kernel_size, self.sigma)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"alpha\", \"lightness\", \"method\", \"kernel_size\", \"sigma\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ShotNoise","title":"class ShotNoise (scale_range=(0.1, 0.3), p=0.5, always_apply=False) [view source on GitHub]","text":"

Apply shot noise to the image by modeling photon counting as a Poisson process.

Shot noise (also known as Poisson noise) occurs in imaging due to the quantum nature of light. When photons hit an imaging sensor, they arrive at random times following Poisson statistics. This transform simulates this physical process in linear light space by: 1. Converting to linear space (removing gamma) 2. Treating each pixel value as an expected photon count 3. Sampling actual photon counts from a Poisson distribution 4. Converting back to display space (reapplying gamma)

The noise characteristics follow real camera behavior: - Noise variance equals signal mean in linear space (Poisson statistics) - Brighter regions have more absolute noise but less relative noise - Darker regions have less absolute noise but more relative noise - Noise is generated independently for each pixel and color channel

Parameters:

Name Type Description scale_range tuple[float, float]

Range for sampling the noise scale factor. Represents the reciprocal of the expected photon count per unit intensity. Higher values mean more noise: - scale = 0.1: ~100 photons per unit intensity (low noise) - scale = 1.0: ~1 photon per unit intensity (moderate noise) - scale = 10.0: ~0.1 photons per unit intensity (high noise) Default: (0.1, 0.3)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • Performs calculations in linear light space (gamma = 2.2)
  • Preserves the image's mean intensity
  • Memory efficient with in-place operations
  • Thread-safe with independent random seeds

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Generate synthetic image\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> # Apply moderate shot noise\n>>> transform = A.ShotNoise(scale_range=(0.1, 1.0), p=1.0)\n>>> noisy_image = transform(image=image)[\"image\"]\n

References

  • Shot noise: https://en.wikipedia.org/wiki/Shot_noise
  • Original paper: https://doi.org/10.1002/andp.19183622304 (Schottky, 1918)
  • Poisson process: https://en.wikipedia.org/wiki/Poisson_point_process
  • Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ShotNoise(ImageOnlyTransform):\n    \"\"\"Apply shot noise to the image by modeling photon counting as a Poisson process.\n\n    Shot noise (also known as Poisson noise) occurs in imaging due to the quantum nature of light.\n    When photons hit an imaging sensor, they arrive at random times following Poisson statistics.\n    This transform simulates this physical process in linear light space by:\n    1. Converting to linear space (removing gamma)\n    2. Treating each pixel value as an expected photon count\n    3. Sampling actual photon counts from a Poisson distribution\n    4. Converting back to display space (reapplying gamma)\n\n    The noise characteristics follow real camera behavior:\n    - Noise variance equals signal mean in linear space (Poisson statistics)\n    - Brighter regions have more absolute noise but less relative noise\n    - Darker regions have less absolute noise but more relative noise\n    - Noise is generated independently for each pixel and color channel\n\n    Args:\n        scale_range (tuple[float, float]): Range for sampling the noise scale factor.\n            Represents the reciprocal of the expected photon count per unit intensity.\n            Higher values mean more noise:\n            - scale = 0.1: ~100 photons per unit intensity (low noise)\n            - scale = 1.0: ~1 photon per unit intensity (moderate noise)\n            - scale = 10.0: ~0.1 photons per unit intensity (high noise)\n            Default: (0.1, 0.3)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Performs calculations in linear light space (gamma = 2.2)\n        - Preserves the image's mean intensity\n        - Memory efficient with in-place operations\n        - Thread-safe with independent random seeds\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Generate synthetic image\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> # Apply moderate shot noise\n        >>> transform = A.ShotNoise(scale_range=(0.1, 1.0), p=1.0)\n        >>> noisy_image = transform(image=image)[\"image\"]\n\n    References:\n        - Shot noise: https://en.wikipedia.org/wiki/Shot_noise\n        - Original paper: https://doi.org/10.1002/andp.19183622304 (Schottky, 1918)\n        - Poisson process: https://en.wikipedia.org/wiki/Poisson_point_process\n        - Gamma correction: https://en.wikipedia.org/wiki/Gamma_correction\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.1, 0.3),\n        p: float = 0.5,\n        always_apply: bool = False,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_range = scale_range\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        random_seed: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.shot_noise(img, scale, np.random.default_rng(random_seed))\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"scale\": self.py_random.uniform(*self.scale_range),\n            \"random_seed\": self.random_generator.integers(0, 2**32 - 1),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"scale_range\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Solarize","title":"class Solarize (threshold=None, threshold_range=(0.5, 0.5), p=0.5, always_apply=None) [view source on GitHub]","text":"

Invert all pixel values above a threshold.

This transform applies a solarization effect to the input image. Solarization is a phenomenon in photography in which the image recorded on a negative or on a photographic print is wholly or partially reversed in tone. Dark areas appear light or light areas appear dark.

In this implementation, all pixel values above a threshold are inverted.

Parameters:

Name Type Description threshold_range tuple[float, float]

Range for solarizing threshold as a fraction of maximum value. The threshold_range should be in the range [0, 1] and will be multiplied by the maximum value of the image type (255 for uint8 images or 1.0 for float images). Default: (0.5, 0.5) (corresponds to 127.5 for uint8 and 0.5 for float32).

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • For uint8 images, pixel values above the threshold are inverted as: 255 - pixel_value
  • For float32 images, pixel values above the threshold are inverted as: 1.0 - pixel_value
  • The threshold is applied to each channel independently
  • The threshold is calculated in two steps:
  • Sample a value from threshold_range
  • Multiply by the image's maximum value:
    • For uint8: threshold = sampled_value * 255
    • For float32: threshold = sampled_value * 1.0
  • This transform can create interesting artistic effects or be used for data augmentation

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Solarize uint8 image with fixed threshold at 50% of max value (127.5)\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n>>>\n# Solarize uint8 image with random threshold between 40-60% of max value (102-153)\n>>> transform = A.Solarize(threshold_range=(0.4, 0.6), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n>>>\n# Solarize float32 image at 50% of max value (0.5)\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)\n>>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n>>> solarized_image = transform(image=image)['image']\n

Mathematical Formulation: Let f be a value sampled from threshold_range (min, max). For each pixel value p: threshold = f * max_value if p > threshold: p_new = max_value - p !!! else p_new = p

Where max_value is 255 for uint8 images and 1.0 for float32 images.\n

See Also: Invert: For inverting all pixel values regardless of a threshold.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Solarize(ImageOnlyTransform):\n    \"\"\"Invert all pixel values above a threshold.\n\n    This transform applies a solarization effect to the input image. Solarization is a phenomenon in\n    photography in which the image recorded on a negative or on a photographic print is wholly or\n    partially reversed in tone. Dark areas appear light or light areas appear dark.\n\n    In this implementation, all pixel values above a threshold are inverted.\n\n    Args:\n        threshold_range (tuple[float, float]): Range for solarizing threshold as a fraction\n            of maximum value. The threshold_range should be in the range [0, 1] and will be multiplied by the\n            maximum value of the image type (255 for uint8 images or 1.0 for float images).\n            Default: (0.5, 0.5) (corresponds to 127.5 for uint8 and 0.5 for float32).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - For uint8 images, pixel values above the threshold are inverted as: 255 - pixel_value\n        - For float32 images, pixel values above the threshold are inverted as: 1.0 - pixel_value\n        - The threshold is applied to each channel independently\n        - The threshold is calculated in two steps:\n          1. Sample a value from threshold_range\n          2. Multiply by the image's maximum value:\n             * For uint8: threshold = sampled_value * 255\n             * For float32: threshold = sampled_value * 1.0\n        - This transform can create interesting artistic effects or be used for data augmentation\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Solarize uint8 image with fixed threshold at 50% of max value (127.5)\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n        >>>\n        # Solarize uint8 image with random threshold between 40-60% of max value (102-153)\n        >>> transform = A.Solarize(threshold_range=(0.4, 0.6), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n        >>>\n        # Solarize float32 image at 50% of max value (0.5)\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> transform = A.Solarize(threshold_range=(0.5, 0.5), p=1.0)\n        >>> solarized_image = transform(image=image)['image']\n\n    Mathematical Formulation:\n        Let f be a value sampled from threshold_range (min, max).\n        For each pixel value p:\n        threshold = f * max_value\n        if p > threshold:\n            p_new = max_value - p\n        else:\n            p_new = p\n\n        Where max_value is 255 for uint8 images and 1.0 for float32 images.\n\n    See Also:\n        Invert: For inverting all pixel values regardless of a threshold.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        threshold: ScaleFloatType | None\n        threshold_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n\n        @staticmethod\n        def normalize_threshold(\n            threshold: ScaleFloatType | None,\n            threshold_range: tuple[float, float],\n        ) -> tuple[float, float]:\n            \"\"\"Convert legacy threshold or use threshold_range, normalizing to [0,1] range.\"\"\"\n            if threshold is not None:\n                warn(\"`threshold` deprecated. Use `threshold_range` instead.\", DeprecationWarning, stacklevel=2)\n                value = to_tuple(threshold, threshold)\n                return (value[0] / 255, value[1] / 255) if value[1] > 1 else value\n            return threshold_range\n\n        @model_validator(mode=\"after\")\n        def process_threshold(self) -> Self:\n            self.threshold_range = self.normalize_threshold(\n                self.threshold,\n                self.threshold_range,\n            )\n            return self\n\n    def __init__(\n        self,\n        threshold: ScaleFloatType | None = None,\n        threshold_range: tuple[float, float] = (0.5, 0.5),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.threshold_range = threshold_range\n\n    def apply(self, img: np.ndarray, threshold: float, **params: Any) -> np.ndarray:\n        return fmain.solarize(img, threshold)\n\n    def get_params(self) -> dict[str, float]:\n        return {\"threshold\": self.py_random.uniform(*self.threshold_range)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"threshold_range\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Spatter","title":"class Spatter (mean=(0.65, 0.65), std=(0.3, 0.3), gauss_sigma=(2, 2), cutout_threshold=(0.68, 0.68), intensity=(0.6, 0.6), mode='rain', color=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply spatter transform. It simulates corruption which can occlude a lens in the form of rain or mud.

Parameters:

Name Type Description mean tuple[float, float] | float

Mean value of normal distribution for generating liquid layer. If single float mean will be sampled from (0, mean) If tuple of float mean will be sampled from range (mean[0], mean[1]). If you want constant value use (mean, mean). Default (0.65, 0.65)

std tuple[float, float] | float

Standard deviation value of normal distribution for generating liquid layer. If single float the number will be sampled from (0, std). If tuple of float std will be sampled from range (std[0], std[1]). If you want constant value use (std, std). Default: (0.3, 0.3).

gauss_sigma tuple[float, float] | floats

Sigma value for gaussian filtering of liquid layer. If single float the number will be sampled from (0, gauss_sigma). If tuple of float gauss_sigma will be sampled from range (gauss_sigma[0], gauss_sigma[1]). If you want constant value use (gauss_sigma, gauss_sigma). Default: (2, 3).

cutout_threshold tuple[float, float] | floats

Threshold for filtering liqued layer (determines number of drops). If single float it will used as cutout_threshold. If single float the number will be sampled from (0, cutout_threshold). If tuple of float cutout_threshold will be sampled from range (cutout_threshold[0], cutout_threshold[1]). If you want constant value use (cutout_threshold, cutout_threshold). Default: (0.68, 0.68).

intensity tuple[float, float] | floats

Intensity of corruption. If single float the number will be sampled from (0, intensity). If tuple of float intensity will be sampled from range (intensity[0], intensity[1]). If you want constant value use (intensity, intensity). Default: (0.6, 0.6).

mode str, or list[str]

Type of corruption. Currently, supported options are 'rain' and 'mud'. If list is provided type of corruption will be sampled list. Default: (\"rain\").

color list of (r, g, b) or dict or None

Corruption elements color. If list uses provided list as color for specified mode. If dict uses provided color for specified mode. Color for each specified mode should be provided in dict. If None uses default colors (rain: (238, 238, 175), mud: (20, 42, 63)).

p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Reference

https://arxiv.org/abs/1903.12261 https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Spatter(ImageOnlyTransform):\n    \"\"\"Apply spatter transform. It simulates corruption which can occlude a lens in the form of rain or mud.\n\n    Args:\n        mean (tuple[float, float] | float): Mean value of normal distribution for generating liquid layer.\n            If single float mean will be sampled from `(0, mean)`\n            If tuple of float mean will be sampled from range `(mean[0], mean[1])`.\n            If you want constant value use (mean, mean).\n            Default (0.65, 0.65)\n        std (tuple[float, float] | float): Standard deviation value of normal distribution for generating liquid layer.\n            If single float the number will be sampled from `(0, std)`.\n            If tuple of float std will be sampled from range `(std[0], std[1])`.\n            If you want constant value use (std, std).\n            Default: (0.3, 0.3).\n        gauss_sigma (tuple[float, float] | floats): Sigma value for gaussian filtering of liquid layer.\n            If single float the number will be sampled from `(0, gauss_sigma)`.\n            If tuple of float gauss_sigma will be sampled from range `(gauss_sigma[0], gauss_sigma[1])`.\n            If you want constant value use (gauss_sigma, gauss_sigma).\n            Default: (2, 3).\n        cutout_threshold (tuple[float, float] | floats): Threshold for filtering liqued layer\n            (determines number of drops). If single float it will used as cutout_threshold.\n            If single float the number will be sampled from `(0, cutout_threshold)`.\n            If tuple of float cutout_threshold will be sampled from range `(cutout_threshold[0], cutout_threshold[1])`.\n            If you want constant value use `(cutout_threshold, cutout_threshold)`.\n            Default: (0.68, 0.68).\n        intensity (tuple[float, float] | floats): Intensity of corruption.\n            If single float the number will be sampled from `(0, intensity)`.\n            If tuple of float intensity will be sampled from range `(intensity[0], intensity[1])`.\n            If you want constant value use `(intensity, intensity)`.\n            Default: (0.6, 0.6).\n        mode (str, or list[str]): Type of corruption. Currently, supported options are 'rain' and 'mud'.\n             If list is provided type of corruption will be sampled list. Default: (\"rain\").\n        color (list of (r, g, b) or dict or None): Corruption elements color.\n            If list uses provided list as color for specified mode.\n            If dict uses provided color for specified mode. Color for each specified mode should be provided in dict.\n            If None uses default colors (rain: (238, 238, 175), mud: (20, 42, 63)).\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://arxiv.org/abs/1903.12261\n        https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        mean: ZeroOneRangeType = (0.65, 0.65)\n        std: ZeroOneRangeType = (0.3, 0.3)\n        gauss_sigma: NonNegativeFloatRangeType = (2, 2)\n        cutout_threshold: ZeroOneRangeType = (0.68, 0.68)\n        intensity: ZeroOneRangeType = (0.6, 0.6)\n        mode: SpatterMode | Sequence[SpatterMode]\n        color: Sequence[int] | dict[str, Sequence[int]] | None = None\n\n        @field_validator(\"mode\")\n        @classmethod\n        def check_mode(\n            cls,\n            mode: SpatterMode | Sequence[SpatterMode],\n        ) -> Sequence[SpatterMode]:\n            if isinstance(mode, str):\n                return [mode]\n            return mode\n\n        @model_validator(mode=\"after\")\n        def check_color(self) -> Self:\n            if self.color is None:\n                self.color = {\"rain\": [238, 238, 175], \"mud\": [20, 42, 63]}\n\n            elif isinstance(self.color, (list, tuple)) and len(self.mode) == 1:\n                if len(self.color) != NUM_RGB_CHANNELS:\n                    msg = \"Color must be a list of three integers for RGB format.\"\n                    raise ValueError(msg)\n                self.color = {self.mode[0]: self.color}\n            elif isinstance(self.color, dict):\n                result = {}\n                for mode in self.mode:\n                    if mode not in self.color:\n                        raise ValueError(f\"Color for mode {mode} is not specified.\")\n                    if len(self.color[mode]) != NUM_RGB_CHANNELS:\n                        raise ValueError(\n                            f\"Color for mode {mode} must be in RGB format.\",\n                        )\n                    result[mode] = self.color[mode]\n            else:\n                msg = \"Color must be a list of RGB values or a dict mapping mode to RGB values.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        mean: ScaleFloatType = (0.65, 0.65),\n        std: ScaleFloatType = (0.3, 0.3),\n        gauss_sigma: ScaleFloatType = (2, 2),\n        cutout_threshold: ScaleFloatType = (0.68, 0.68),\n        intensity: ScaleFloatType = (0.6, 0.6),\n        mode: SpatterMode | Sequence[SpatterMode] = \"rain\",\n        color: Sequence[int] | dict[str, Sequence[int]] | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.mean = cast(tuple[float, float], mean)\n        self.std = cast(tuple[float, float], std)\n        self.gauss_sigma = cast(tuple[float, float], gauss_sigma)\n        self.cutout_threshold = cast(tuple[float, float], cutout_threshold)\n        self.intensity = cast(tuple[float, float], intensity)\n        self.mode = mode\n        self.color = cast(dict[str, Sequence[int]], color)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        non_mud: np.ndarray,\n        mud: np.ndarray,\n        drops: np.ndarray,\n        mode: SpatterMode,\n        **params: dict[str, Any],\n    ) -> np.ndarray:\n        non_rgb_error(img)\n        return fmain.spatter(img, non_mud, mud, drops, mode)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        mean = self.py_random.uniform(*self.mean)\n        std = self.py_random.uniform(*self.std)\n        cutout_threshold = self.py_random.uniform(*self.cutout_threshold)\n        sigma = self.py_random.uniform(*self.gauss_sigma)\n        mode = self.py_random.choice(self.mode)\n        intensity = self.py_random.uniform(*self.intensity)\n        color = np.array(self.color[mode]) / 255.0\n\n        liquid_layer = self.random_generator.normal(\n            size=(height, width),\n            loc=mean,\n            scale=std,\n        )\n        liquid_layer = gaussian_filter(liquid_layer, sigma=sigma, mode=\"nearest\")\n        liquid_layer[liquid_layer < cutout_threshold] = 0\n\n        if mode == \"rain\":\n            liquid_layer = clip(liquid_layer * 255, np.uint8, inplace=False)\n            dist = 255 - cv2.Canny(liquid_layer, 50, 150)\n            dist = cv2.distanceTransform(dist, cv2.DIST_L2, 5)\n            _, dist = cv2.threshold(dist, 20, 20, cv2.THRESH_TRUNC)\n            dist = clip(fblur.blur(dist, 3), np.uint8, inplace=True)\n            dist = fmain.equalize(dist)\n\n            ker = np.array([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]])\n            dist = fmain.convolve(dist, ker)\n            dist = fblur.blur(dist, 3).astype(np.float32)\n\n            m = liquid_layer * dist\n            m *= 1 / np.max(m, axis=(0, 1))\n\n            drops = m[:, :, None] * color * intensity\n            mud = None\n            non_mud = None\n        else:\n            m = np.where(liquid_layer > cutout_threshold, 1, 0)\n            m = gaussian_filter(m.astype(np.float32), sigma=sigma, mode=\"nearest\")\n            m[m < 1.2 * cutout_threshold] = 0\n            m = m[..., np.newaxis]\n\n            mud = m * color\n            non_mud = 1 - m\n            drops = None\n\n        return {\n            \"non_mud\": non_mud,\n            \"mud\": mud,\n            \"drops\": drops,\n            \"mode\": mode,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str, str, str, str]:\n        return (\n            \"mean\",\n            \"std\",\n            \"gauss_sigma\",\n            \"intensity\",\n            \"cutout_threshold\",\n            \"mode\",\n            \"color\",\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.Superpixels","title":"class Superpixels (p_replace=(0, 0.1), n_segments=(100, 100), max_size=128, interpolation=1, p=0.5, always_apply=None) [view source on GitHub]","text":"

Transform images partially/completely to their superpixel representation.

Parameters:

Name Type Description p_replace tuple[float, float] | float

Defines for any segment the probability that the pixels within that segment are replaced by their average color (otherwise, the pixels are not changed).

  • A probability of 0.0 would mean, that the pixels in no segment are replaced by their average color (image is not changed at all).
  • A probability of 0.5 would mean, that around half of all segments are replaced by their average color.
  • A probability of 1.0 would mean, that all segments are replaced by their average color (resulting in a voronoi image).

Behavior based on chosen data types for this parameter: * If a float, then that float will always be used. * If tuple (a, b), then a random probability will be sampled from the interval [a, b] per image. Default: (0.1, 0.3)

n_segments tuple[int, int] | int

Rough target number of how many superpixels to generate. The algorithm may deviate from this number. Lower value will lead to coarser superpixels. Higher values are computationally more intensive and will hence lead to a slowdown. If tuple (a, b), then a value from the discrete interval [a..b] will be sampled per image. Default: (15, 120)

max_size int | None

Maximum image size at which the augmentation is performed. If the width or height of an image exceeds this value, it will be downscaled before the augmentation so that the longest side matches max_size. This is done to speed up the process. The final output image has the same size as the input image. Note that in case p_replace is below 1.0, the down-/upscaling will affect the not-replaced pixels too. Use None to apply no down-/upscaling. Default: 128

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: Any

Note

  • This transform can significantly change the visual appearance of the image.
  • The transform makes use of a superpixel algorithm, which tends to be slow. If performance is a concern, consider using max_size to limit the image size.
  • The effect of this transform can vary greatly depending on the p_replace and n_segments parameters.
  • When p_replace is high, the image can become highly abstracted, resembling a voronoi diagram.
  • The transform preserves the original image type (uint8 or float32).

Mathematical Formulation: 1. The image is segmented into approximately n_segments superpixels using the SLIC algorithm. 2. For each superpixel: - With probability p_replace, all pixels in the superpixel are replaced with their mean color. - With probability 1 - p_replace, the superpixel is left unchanged. 3. If the image was resized due to max_size, it is resized back to its original dimensions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-superpixels-with-default-parameters","title":"Apply superpixels with default parameters","text":"Python
>>> transform = A.Superpixels(p=1.0)\n>>> augmented_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms--apply-superpixels-with-custom-parameters","title":"Apply superpixels with custom parameters","text":"Python
>>> transform = A.Superpixels(\n...     p_replace=(0.5, 0.7),\n...     n_segments=(50, 100),\n...     max_size=None,\n...     interpolation=cv2.INTER_NEAREST,\n...     p=1.0\n... )\n>>> augmented_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class Superpixels(ImageOnlyTransform):\n    \"\"\"Transform images partially/completely to their superpixel representation.\n\n    Args:\n        p_replace (tuple[float, float] | float): Defines for any segment the probability that the pixels within that\n            segment are replaced by their average color (otherwise, the pixels are not changed).\n\n\n            * A probability of ``0.0`` would mean, that the pixels in no\n                segment are replaced by their average color (image is not\n                changed at all).\n            * A probability of ``0.5`` would mean, that around half of all\n                segments are replaced by their average color.\n            * A probability of ``1.0`` would mean, that all segments are\n                replaced by their average color (resulting in a voronoi\n                image).\n\n            Behavior based on chosen data types for this parameter:\n            * If a ``float``, then that ``float`` will always be used.\n            * If ``tuple`` ``(a, b)``, then a random probability will be\n            sampled from the interval ``[a, b]`` per image.\n            Default: (0.1, 0.3)\n\n        n_segments (tuple[int, int] | int): Rough target number of how many superpixels to generate.\n            The algorithm may deviate from this number.\n            Lower value will lead to coarser superpixels.\n            Higher values are computationally more intensive and will hence lead to a slowdown.\n            If tuple ``(a, b)``, then a value from the discrete interval ``[a..b]`` will be sampled per image.\n            Default: (15, 120)\n\n        max_size (int | None): Maximum image size at which the augmentation is performed.\n            If the width or height of an image exceeds this value, it will be\n            downscaled before the augmentation so that the longest side matches `max_size`.\n            This is done to speed up the process. The final output image has the same size as the input image.\n            Note that in case `p_replace` is below ``1.0``,\n            the down-/upscaling will affect the not-replaced pixels too.\n            Use ``None`` to apply no down-/upscaling.\n            Default: 128\n\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform can significantly change the visual appearance of the image.\n        - The transform makes use of a superpixel algorithm, which tends to be slow.\n        If performance is a concern, consider using `max_size` to limit the image size.\n        - The effect of this transform can vary greatly depending on the `p_replace` and `n_segments` parameters.\n        - When `p_replace` is high, the image can become highly abstracted, resembling a voronoi diagram.\n        - The transform preserves the original image type (uint8 or float32).\n\n    Mathematical Formulation:\n        1. The image is segmented into approximately `n_segments` superpixels using the SLIC algorithm.\n        2. For each superpixel:\n        - With probability `p_replace`, all pixels in the superpixel are replaced with their mean color.\n        - With probability `1 - p_replace`, the superpixel is left unchanged.\n        3. If the image was resized due to `max_size`, it is resized back to its original dimensions.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply superpixels with default parameters\n        >>> transform = A.Superpixels(p=1.0)\n        >>> augmented_image = transform(image=image)['image']\n\n        # Apply superpixels with custom parameters\n        >>> transform = A.Superpixels(\n        ...     p_replace=(0.5, 0.7),\n        ...     n_segments=(50, 100),\n        ...     max_size=None,\n        ...     interpolation=cv2.INTER_NEAREST,\n        ...     p=1.0\n        ... )\n        >>> augmented_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        p_replace: ZeroOneRangeType\n        n_segments: OnePlusIntRangeType\n        max_size: int | None = Field(ge=1)\n        interpolation: InterpolationType\n\n    def __init__(\n        self,\n        p_replace: ScaleFloatType = (0, 0.1),\n        n_segments: ScaleIntType = (100, 100),\n        max_size: int | None = 128,\n        interpolation: int = cv2.INTER_LINEAR,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.p_replace = cast(tuple[float, float], p_replace)\n        self.n_segments = cast(tuple[int, int], n_segments)\n        self.max_size = max_size\n        self.interpolation = interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"p_replace\", \"n_segments\", \"max_size\", \"interpolation\"\n\n    def get_params(self) -> dict[str, Any]:\n        n_segments = self.py_random.randint(*self.n_segments)\n        p = self.py_random.uniform(*self.p_replace)\n        return {\n            \"replace_samples\": self.random_generator.random(n_segments) < p,\n            \"n_segments\": n_segments,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        replace_samples: Sequence[bool],\n        n_segments: int,\n        **kwargs: Any,\n    ) -> np.ndarray:\n        return fmain.superpixels(\n            img,\n            n_segments,\n            replace_samples,\n            self.max_size,\n            self.interpolation,\n        )\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToFloat","title":"class ToFloat (max_value=None, p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert the input image to a floating-point representation.

This transform divides pixel values by max_value to get a float32 output array where all values lie in the range [0, 1.0]. It's useful for normalizing image data before feeding it into neural networks or other algorithms that expect float input.

Parameters:

Name Type Description max_value float | None

The maximum possible input value. If None, the transform will try to infer the maximum value by inspecting the data type of the input image: - uint8: 255 - uint16: 65535 - uint32: 4294967295 - float32: 1.0 Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: uint8, uint16, uint32, float32

Returns:

Type Description np.ndarray

Image in floating point representation, with values in range [0, 1.0].

Note

  • If the input image is already float32 with values in [0, 1], it will be returned unchanged.
  • For integer types (uint8, uint16, uint32), the function will scale the values to [0, 1] range.
  • The output will always be float32, regardless of the input type.
  • This transform is often used as a preprocessing step before applying other transformations or feeding the image into a neural network.

Exceptions:

Type Description TypeError

If the input image data type is not supported.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Convert uint8 image to float\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ToFloat(max_value=None)\n>>> float_image = transform(image=image)['image']\n>>> assert float_image.dtype == np.float32\n>>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n>>>\n# Convert uint16 image to float with custom max_value\n>>> image = np.random.randint(0, 4096, (100, 100, 3), dtype=np.uint16)\n>>> transform = A.ToFloat(max_value=4095)\n>>> float_image = transform(image=image)['image']\n>>> assert float_image.dtype == np.float32\n>>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n

See Also: FromFloat: The inverse operation, converting from float back to the original data type.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToFloat(ImageOnlyTransform):\n    \"\"\"Convert the input image to a floating-point representation.\n\n    This transform divides pixel values by `max_value` to get a float32 output array\n    where all values lie in the range [0, 1.0]. It's useful for normalizing image data\n    before feeding it into neural networks or other algorithms that expect float input.\n\n    Args:\n        max_value (float | None): The maximum possible input value. If None, the transform\n            will try to infer the maximum value by inspecting the data type of the input image:\n            - uint8: 255\n            - uint16: 65535\n            - uint32: 4294967295\n            - float32: 1.0\n            Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, uint16, uint32, float32\n\n    Returns:\n        np.ndarray: Image in floating point representation, with values in range [0, 1.0].\n\n    Note:\n        - If the input image is already float32 with values in [0, 1], it will be returned unchanged.\n        - For integer types (uint8, uint16, uint32), the function will scale the values to [0, 1] range.\n        - The output will always be float32, regardless of the input type.\n        - This transform is often used as a preprocessing step before applying other transformations\n          or feeding the image into a neural network.\n\n    Raises:\n        TypeError: If the input image data type is not supported.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Convert uint8 image to float\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ToFloat(max_value=None)\n        >>> float_image = transform(image=image)['image']\n        >>> assert float_image.dtype == np.float32\n        >>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n        >>>\n        # Convert uint16 image to float with custom max_value\n        >>> image = np.random.randint(0, 4096, (100, 100, 3), dtype=np.uint16)\n        >>> transform = A.ToFloat(max_value=4095)\n        >>> float_image = transform(image=image)['image']\n        >>> assert float_image.dtype == np.float32\n        >>> assert 0 <= float_image.min() <= float_image.max() <= 1.0\n\n    See Also:\n        FromFloat: The inverse operation, converting from float back to the original data type.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        max_value: float | None\n\n    def __init__(\n        self,\n        max_value: float | None = None,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply)\n        self.max_value = max_value\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return to_float(img, self.max_value)\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"max_value\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToGray","title":"class ToGray (num_output_channels=3, method='weighted_average', always_apply=None, p=0.5) [view source on GitHub]","text":"

Convert an image to grayscale and optionally replicate the grayscale channel.

This transform first converts a color image to a single-channel grayscale image using various methods, then replicates the grayscale channel if num_output_channels is greater than 1.

Parameters:

Name Type Description num_output_channels int

The number of channels in the output image. If greater than 1, the grayscale channel will be replicated. Default: 3.

method Literal[\"weighted_average\", \"from_lab\", \"desaturation\", \"average\", \"max\", \"pca\"]

The method used for grayscale conversion: - \"weighted_average\": Uses a weighted sum of RGB channels (0.299R + 0.587G + 0.114B). Works only with 3-channel images. Provides realistic results based on human perception. - \"from_lab\": Extracts the L channel from the LAB color space. Works only with 3-channel images. Gives perceptually uniform results. - \"desaturation\": Averages the maximum and minimum values across channels. Works with any number of channels. Fast but may not preserve perceived brightness well. - \"average\": Simple average of all channels. Works with any number of channels. Fast but may not give realistic results. - \"max\": Takes the maximum value across all channels. Works with any number of channels. Tends to produce brighter results. - \"pca\": Applies Principal Component Analysis to reduce channels. Works with any number of channels. Can preserve more information but is computationally intensive.

p float

Probability of applying the transform. Default: 0.5.

Exceptions:

Type Description TypeError

If the input image doesn't have 3 channels for methods that require it.

Note

  • The transform first converts the input image to single-channel grayscale, then replicates this channel if num_output_channels > 1.
  • \"weighted_average\" and \"from_lab\" are typically used in image processing and computer vision applications where accurate representation of human perception is important.
  • \"desaturation\" and \"average\" are often used in simple image manipulation tools or when computational speed is a priority.
  • \"max\" method can be useful in scenarios where preserving bright features is important, such as in some medical imaging applications.
  • \"pca\" might be used in advanced image analysis tasks or when dealing with hyperspectral images.

Image types: uint8, float32

Returns:

Type Description np.ndarray

Grayscale image with the specified number of channels.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToGray(ImageOnlyTransform):\n    \"\"\"Convert an image to grayscale and optionally replicate the grayscale channel.\n\n    This transform first converts a color image to a single-channel grayscale image using various methods,\n    then replicates the grayscale channel if num_output_channels is greater than 1.\n\n    Args:\n        num_output_channels (int): The number of channels in the output image. If greater than 1,\n            the grayscale channel will be replicated. Default: 3.\n        method (Literal[\"weighted_average\", \"from_lab\", \"desaturation\", \"average\", \"max\", \"pca\"]):\n            The method used for grayscale conversion:\n            - \"weighted_average\": Uses a weighted sum of RGB channels (0.299R + 0.587G + 0.114B).\n              Works only with 3-channel images. Provides realistic results based on human perception.\n            - \"from_lab\": Extracts the L channel from the LAB color space.\n              Works only with 3-channel images. Gives perceptually uniform results.\n            - \"desaturation\": Averages the maximum and minimum values across channels.\n              Works with any number of channels. Fast but may not preserve perceived brightness well.\n            - \"average\": Simple average of all channels.\n              Works with any number of channels. Fast but may not give realistic results.\n            - \"max\": Takes the maximum value across all channels.\n              Works with any number of channels. Tends to produce brighter results.\n            - \"pca\": Applies Principal Component Analysis to reduce channels.\n              Works with any number of channels. Can preserve more information but is computationally intensive.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Raises:\n        TypeError: If the input image doesn't have 3 channels for methods that require it.\n\n    Note:\n        - The transform first converts the input image to single-channel grayscale, then replicates\n          this channel if num_output_channels > 1.\n        - \"weighted_average\" and \"from_lab\" are typically used in image processing and computer vision\n          applications where accurate representation of human perception is important.\n        - \"desaturation\" and \"average\" are often used in simple image manipulation tools or when\n          computational speed is a priority.\n        - \"max\" method can be useful in scenarios where preserving bright features is important,\n          such as in some medical imaging applications.\n        - \"pca\" might be used in advanced image analysis tasks or when dealing with hyperspectral images.\n\n    Image types:\n        uint8, float32\n\n    Returns:\n        np.ndarray: Grayscale image with the specified number of channels.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_output_channels: int = Field(\n            default=3,\n            description=\"The number of output channels.\",\n            ge=1,\n        )\n        method: Literal[\n            \"weighted_average\",\n            \"from_lab\",\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        ]\n\n    def __init__(\n        self,\n        num_output_channels: int = 3,\n        method: Literal[\n            \"weighted_average\",\n            \"from_lab\",\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        ] = \"weighted_average\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_output_channels = num_output_channels\n        self.method = method\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_grayscale_image(img):\n            warnings.warn(\"The image is already gray.\", stacklevel=2)\n            return img\n\n        num_channels = get_num_channels(img)\n\n        if num_channels != NUM_RGB_CHANNELS and self.method not in {\n            \"desaturation\",\n            \"average\",\n            \"max\",\n            \"pca\",\n        }:\n            msg = \"ToGray transformation expects 3-channel images.\"\n            raise TypeError(msg)\n\n        return fmain.to_gray(img, self.num_output_channels, self.method)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_output_channels\", \"method\"\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToRGB","title":"class ToRGB (num_output_channels=3, p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert an input image from grayscale to RGB format.

Parameters:

Name Type Description num_output_channels int

The number of channels in the output image. Default: 3.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1

Note

  • For single-channel (grayscale) images, the channel is replicated to create an RGB image.
  • If the input is already a 3-channel RGB image, it is returned unchanged.
  • This transform does not change the data type of the image (e.g., uint8 remains uint8).

Exceptions:

Type Description TypeError

If the input image has more than 1 channel.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n>>> # Convert a grayscale image to RGB\n>>> transform = A.Compose([A.ToRGB(p=1.0)])\n>>> grayscale_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n>>> rgb_image = transform(image=grayscale_image)['image']\n>>> assert rgb_image.shape == (100, 100, 3)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToRGB(ImageOnlyTransform):\n    \"\"\"Convert an input image from grayscale to RGB format.\n\n    Args:\n        num_output_channels (int): The number of channels in the output image. Default: 3.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1\n\n    Note:\n        - For single-channel (grayscale) images, the channel is replicated to create an RGB image.\n        - If the input is already a 3-channel RGB image, it is returned unchanged.\n        - This transform does not change the data type of the image (e.g., uint8 remains uint8).\n\n    Raises:\n        TypeError: If the input image has more than 1 channel.\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        >>> # Convert a grayscale image to RGB\n        >>> transform = A.Compose([A.ToRGB(p=1.0)])\n        >>> grayscale_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n        >>> rgb_image = transform(image=grayscale_image)['image']\n        >>> assert rgb_image.shape == (100, 100, 3)\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_output_channels: int = Field(ge=1)\n\n    def __init__(\n        self,\n        num_output_channels: int = 3,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.num_output_channels = num_output_channels\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_rgb_image(img):\n            warnings.warn(\"The image is already an RGB.\", stacklevel=2)\n            return np.ascontiguousarray(img)\n        if not is_grayscale_image(img):\n            msg = \"ToRGB transformation expects 2-dim images or 3-dim with the last dimension equal to 1.\"\n            raise TypeError(msg)\n\n        return fmain.grayscale_to_multichannel(\n            img,\n            num_output_channels=self.num_output_channels,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str]:\n        return (\"num_output_channels\",)\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.ToSepia","title":"class ToSepia (p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply a sepia filter to the input image.

This transform converts a color image to a sepia tone, giving it a warm, brownish tint that is reminiscent of old photographs. The sepia effect is achieved by applying a specific color transformation matrix to the RGB channels of the input image. For grayscale images, the transform is a no-op and returns the original image.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Number of channels: 1,3

Note

  • The sepia effect only works with RGB images (3 channels). For grayscale images, the original image is returned unchanged since the sepia transformation would have no visible effect when R=G=B.
  • The sepia effect is created using a fixed color transformation matrix: [[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]]
  • The output image will have the same data type as the input image.
  • For float32 images, ensure the input values are in the range [0, 1].

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n# Apply sepia effect to a uint8 RGB image\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ToSepia(p=1.0)\n>>> sepia_image = transform(image=image)['image']\n>>> assert sepia_image.shape == image.shape\n>>> assert sepia_image.dtype == np.uint8\n>>>\n# Apply sepia effect to a float32 RGB image\n>>> image = np.random.rand(100, 100, 3).astype(np.float32)\n>>> transform = A.ToSepia(p=1.0)\n>>> sepia_image = transform(image=image)['image']\n>>> assert sepia_image.shape == image.shape\n>>> assert sepia_image.dtype == np.float32\n>>> assert 0 <= sepia_image.min() <= sepia_image.max() <= 1.0\n>>>\n# No effect on grayscale images\n>>> gray_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n>>> transform = A.ToSepia(p=1.0)\n>>> result = transform(image=gray_image)['image']\n>>> assert np.array_equal(result, gray_image)\n

Mathematical Formulation: Given an input pixel [R, G, B], the sepia tone is calculated as: R_sepia = 0.393R + 0.769G + 0.189B G_sepia = 0.349R + 0.686G + 0.168B B_sepia = 0.272R + 0.534G + 0.131*B

For grayscale images where R=G=B, this transformation would result in a simple\nscaling of the original value, so we skip it.\n\nThe output values are clipped to the valid range for the image's data type.\n

See Also: ToGray: For converting images to grayscale instead of sepia.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class ToSepia(ImageOnlyTransform):\n    \"\"\"Apply a sepia filter to the input image.\n\n    This transform converts a color image to a sepia tone, giving it a warm, brownish tint\n    that is reminiscent of old photographs. The sepia effect is achieved by applying a\n    specific color transformation matrix to the RGB channels of the input image.\n    For grayscale images, the transform is a no-op and returns the original image.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        1,3\n\n    Note:\n        - The sepia effect only works with RGB images (3 channels). For grayscale images,\n          the original image is returned unchanged since the sepia transformation would\n          have no visible effect when R=G=B.\n        - The sepia effect is created using a fixed color transformation matrix:\n          [[0.393, 0.769, 0.189],\n           [0.349, 0.686, 0.168],\n           [0.272, 0.534, 0.131]]\n        - The output image will have the same data type as the input image.\n        - For float32 images, ensure the input values are in the range [0, 1].\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        # Apply sepia effect to a uint8 RGB image\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> sepia_image = transform(image=image)['image']\n        >>> assert sepia_image.shape == image.shape\n        >>> assert sepia_image.dtype == np.uint8\n        >>>\n        # Apply sepia effect to a float32 RGB image\n        >>> image = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> sepia_image = transform(image=image)['image']\n        >>> assert sepia_image.shape == image.shape\n        >>> assert sepia_image.dtype == np.float32\n        >>> assert 0 <= sepia_image.min() <= sepia_image.max() <= 1.0\n        >>>\n        # No effect on grayscale images\n        >>> gray_image = np.random.randint(0, 256, (100, 100), dtype=np.uint8)\n        >>> transform = A.ToSepia(p=1.0)\n        >>> result = transform(image=gray_image)['image']\n        >>> assert np.array_equal(result, gray_image)\n\n    Mathematical Formulation:\n        Given an input pixel [R, G, B], the sepia tone is calculated as:\n        R_sepia = 0.393*R + 0.769*G + 0.189*B\n        G_sepia = 0.349*R + 0.686*G + 0.168*B\n        B_sepia = 0.272*R + 0.534*G + 0.131*B\n\n        For grayscale images where R=G=B, this transformation would result in a simple\n        scaling of the original value, so we skip it.\n\n        The output values are clipped to the valid range for the image's data type.\n\n    See Also:\n        ToGray: For converting images to grayscale instead of sepia.\n    \"\"\"\n\n    def __init__(self, p: float = 0.5, always_apply: bool | None = None):\n        super().__init__(p, always_apply)\n        self.sepia_transformation_matrix = np.array(\n            [[0.393, 0.769, 0.189], [0.349, 0.686, 0.168], [0.272, 0.534, 0.131]],\n        )\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        if is_grayscale_image(img):\n            return img\n\n        if not is_rgb_image(img):\n            msg = \"ToSepia transformation expects 1 or 3-channel images.\"\n            raise TypeError(msg)\n        return fmain.linear_transformation_rgb(img, self.sepia_transformation_matrix)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.UniformParams","title":"class UniformParams [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class UniformParams(NoiseParamsBase):\n    noise_type: Literal[\"uniform\"] = \"uniform\"\n    ranges: list[Sequence[float]] = Field(\n        description=\"List of (min, max) ranges for each channel\",\n        min_length=1,\n    )\n\n    @field_validator(\"ranges\", mode=\"after\")\n    @classmethod\n    def validate_ranges(cls, v: list[Sequence[float]]) -> list[tuple[float, float]]:\n        result = []\n        for range_values in v:\n            if len(range_values) != PAIR:\n                raise ValueError(\"Each range must have exactly 2 values\")\n            min_val, max_val = range_values\n            if not (-1 <= min_val <= max_val <= 1):\n                raise ValueError(\"Range values must be in [-1, 1] and min <= max\")\n            result.append((float(min_val), float(max_val)))\n        return result\n
"},{"location":"api_reference/augmentations/transforms/#albumentations.augmentations.transforms.UnsharpMask","title":"class UnsharpMask (blur_limit=(3, 7), sigma_limit=0.0, alpha=(0.2, 0.5), threshold=10, p=0.5, always_apply=None) [view source on GitHub]","text":"

Sharpen the input image using Unsharp Masking processing and overlays the result with the original image.

Unsharp masking is a technique that enhances edge contrast in an image, creating the illusion of increased sharpness. This transform applies Gaussian blur to create a blurred version of the image, then uses this to create a mask which is combined with the original image to enhance edges and fine details.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

maximum Gaussian kernel size for blurring the input image. Must be zero or odd and in range [0, inf). If set to 0 it will be computed from sigma as round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1. If set single value blur_limit will be in range (0, blur_limit). Default: (3, 7).

sigma_limit tuple[float, float] | float

Gaussian kernel standard deviation. Must be in range [0, inf). If set single value sigma_limit will be in range (0, sigma_limit). If set to 0 sigma will be computed as sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8. Default: 0.

alpha tuple[float, float]

range to choose the visibility of the sharpened image. At 0, only the original image is visible, at 1.0 only its sharpened version is visible. Default: (0.2, 0.5).

threshold int

Value to limit sharpening only for areas with high pixel difference between original image and it's smoothed version. Higher threshold means less sharpening on flat areas. Must be in range [0, 255]. Default: 10.

p float

probability of applying the transform. Default: 0.5.

Targets

image, volume

Image types: uint8, float32

Note

  • The algorithm creates a mask M = (I - G) * alpha, where I is the original image and G is the Gaussian blurred version.
  • The final image is computed as: output = I + M if |I - G| > threshold, else I.
  • Higher alpha values increase the strength of the sharpening effect.
  • Higher threshold values limit the sharpening effect to areas with more significant edges or details.
  • The blur_limit and sigma_limit parameters control the Gaussian blur used to create the mask.

References

  • https://en.wikipedia.org/wiki/Unsharp_masking
  • https://arxiv.org/pdf/2107.10833.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>>\n# Apply UnsharpMask with default parameters\n>>> transform = A.UnsharpMask(p=1.0)\n>>> sharpened_image = transform(image=image)['image']\n>>>\n# Apply UnsharpMask with custom parameters\n>>> transform = A.UnsharpMask(\n...     blur_limit=(3, 7),\n...     sigma_limit=(0.1, 0.5),\n...     alpha=(0.2, 0.7),\n...     threshold=15,\n...     p=1.0\n... )\n>>> sharpened_image = transform(image=image)['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms.py Python
class UnsharpMask(ImageOnlyTransform):\n    \"\"\"Sharpen the input image using Unsharp Masking processing and overlays the result with the original image.\n\n    Unsharp masking is a technique that enhances edge contrast in an image, creating the illusion of increased\n        sharpness.\n    This transform applies Gaussian blur to create a blurred version of the image, then uses this to create a mask\n    which is combined with the original image to enhance edges and fine details.\n\n    Args:\n        blur_limit (tuple[int, int] | int): maximum Gaussian kernel size for blurring the input image.\n            Must be zero or odd and in range [0, inf). If set to 0 it will be computed from sigma\n            as `round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1`.\n            If set single value `blur_limit` will be in range (0, blur_limit).\n            Default: (3, 7).\n        sigma_limit (tuple[float, float] | float): Gaussian kernel standard deviation. Must be in range [0, inf).\n            If set single value `sigma_limit` will be in range (0, sigma_limit).\n            If set to 0 sigma will be computed as `sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`. Default: 0.\n        alpha (tuple[float, float]): range to choose the visibility of the sharpened image.\n            At 0, only the original image is visible, at 1.0 only its sharpened version is visible.\n            Default: (0.2, 0.5).\n        threshold (int): Value to limit sharpening only for areas with high pixel difference between original image\n            and it's smoothed version. Higher threshold means less sharpening on flat areas.\n            Must be in range [0, 255]. Default: 10.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The algorithm creates a mask M = (I - G) * alpha, where I is the original image and G is the Gaussian\n            blurred version.\n        - The final image is computed as: output = I + M if |I - G| > threshold, else I.\n        - Higher alpha values increase the strength of the sharpening effect.\n        - Higher threshold values limit the sharpening effect to areas with more significant edges or details.\n        - The blur_limit and sigma_limit parameters control the Gaussian blur used to create the mask.\n\n    References:\n        - https://en.wikipedia.org/wiki/Unsharp_masking\n        - https://arxiv.org/pdf/2107.10833.pdf\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>>\n        # Apply UnsharpMask with default parameters\n        >>> transform = A.UnsharpMask(p=1.0)\n        >>> sharpened_image = transform(image=image)['image']\n        >>>\n        # Apply UnsharpMask with custom parameters\n        >>> transform = A.UnsharpMask(\n        ...     blur_limit=(3, 7),\n        ...     sigma_limit=(0.1, 0.5),\n        ...     alpha=(0.2, 0.7),\n        ...     threshold=15,\n        ...     p=1.0\n        ... )\n        >>> sharpened_image = transform(image=image)['image']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        sigma_limit: NonNegativeFloatRangeType\n        alpha: ZeroOneRangeType\n        threshold: int = Field(ge=0, le=255)\n        blur_limit: ScaleIntType\n\n        @field_validator(\"blur_limit\")\n        @classmethod\n        def process_blur(\n            cls,\n            value: ScaleIntType,\n            info: ValidationInfo,\n        ) -> tuple[int, int]:\n            return fblur.process_blur_limit(value, info, min_value=3)\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_limit: ScaleFloatType = 0.0,\n        alpha: ScaleFloatType = (0.2, 0.5),\n        threshold: int = 10,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_limit = cast(tuple[float, float], sigma_limit)\n        self.alpha = cast(tuple[float, float], alpha)\n        self.threshold = threshold\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"ksize\": self.py_random.randrange(\n                self.blur_limit[0],\n                self.blur_limit[1] + 1,\n                2,\n            ),\n            \"sigma\": self.py_random.uniform(*self.sigma_limit),\n            \"alpha\": self.py_random.uniform(*self.alpha),\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        ksize: int,\n        sigma: int,\n        alpha: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fmain.unsharp_mask(\n            img,\n            ksize,\n            sigma=sigma,\n            alpha=alpha,\n            threshold=self.threshold,\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"blur_limit\", \"sigma_limit\", \"alpha\", \"threshold\"\n
"},{"location":"api_reference/augmentations/blur/","title":"Index","text":"
  • Blur transforms (albumentations.augmentations.blur.transforms)
"},{"location":"api_reference/augmentations/blur/functional/","title":"Blur functional transforms (augmentations.blur.functional)","text":""},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.create_motion_kernel","title":"def create_motion_kernel (kernel_size, angle, direction, allow_shifted, random_state) [view source on GitHub]","text":"

Create a motion blur kernel.

Parameters:

Name Type Description kernel_size int

Size of the kernel (must be odd)

angle float

Angle in degrees (counter-clockwise)

direction float

Blur direction (-1.0 to 1.0)

allow_shifted bool

Allow kernel to be randomly shifted from center

random_state Random

Python's random.Random instance

Returns:

Type Description np.ndarray

Motion blur kernel

Source code in albumentations/augmentations/blur/functional.py Python
def create_motion_kernel(\n    kernel_size: int,\n    angle: float,\n    direction: float,\n    allow_shifted: bool,\n    random_state: Random,\n) -> np.ndarray:\n    \"\"\"Create a motion blur kernel.\n\n    Args:\n        kernel_size: Size of the kernel (must be odd)\n        angle: Angle in degrees (counter-clockwise)\n        direction: Blur direction (-1.0 to 1.0)\n        allow_shifted: Allow kernel to be randomly shifted from center\n        random_state: Python's random.Random instance\n\n    Returns:\n        Motion blur kernel\n    \"\"\"\n    kernel = np.zeros((kernel_size, kernel_size), dtype=np.float32)\n    center = kernel_size // 2\n\n    # Convert angle to radians\n    angle_rad = np.deg2rad(angle)\n\n    # Calculate direction vector\n    dx = np.cos(angle_rad)\n    dy = np.sin(angle_rad)\n\n    # Create line points with direction bias\n    line_length = kernel_size // 2\n    t = np.linspace(-line_length, line_length, kernel_size * 2)\n\n    # Apply direction bias\n    if direction != 0:\n        t = t * (1 + abs(direction))\n        if direction < 0:\n            t = t * -1\n\n    # Generate line coordinates\n    x = center + dx * t\n    y = center + dy * t\n\n    # Apply random shift if allowed\n    if allow_shifted and random_state is not None:\n        shift_x = random_state.uniform(-1, 1) * line_length / 2\n        shift_y = random_state.uniform(-1, 1) * line_length / 2\n        x += shift_x\n        y += shift_y\n\n    # Round coordinates and clip to kernel bounds\n    x = np.clip(np.round(x), 0, kernel_size - 1).astype(int)\n    y = np.clip(np.round(y), 0, kernel_size - 1).astype(int)\n\n    # Keep only unique points to avoid multiple assignments\n    points = np.unique(np.column_stack([y, x]), axis=0)\n    kernel[points[:, 0], points[:, 1]] = 1\n\n    # Ensure at least one point is set\n    if not kernel.any():\n        kernel[center, center] = 1\n\n    return kernel\n
"},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.process_blur_limit","title":"def process_blur_limit (value, info, min_value=0) [view source on GitHub]","text":"

Process blur limit to ensure valid kernel sizes.

Source code in albumentations/augmentations/blur/functional.py Python
def process_blur_limit(value: ScaleIntType, info: ValidationInfo, min_value: int = 0) -> tuple[int, int]:\n    \"\"\"Process blur limit to ensure valid kernel sizes.\"\"\"\n    result = value if isinstance(value, Sequence) else (min_value, value)\n\n    result = _ensure_min_value(result, min_value, info.field_name)\n    result = _ensure_odd_values(result, info.field_name)\n\n    if result[0] > result[1]:\n        final_result = (result[1], result[1])\n        warn(\n            f\"{info.field_name}: Invalid range {result} (min > max). \"\n            f\"Range automatically adjusted to {final_result}.\",\n            UserWarning,\n            stacklevel=2,\n        )\n        return final_result\n\n    return result\n
"},{"location":"api_reference/augmentations/blur/functional/#albumentations.augmentations.blur.functional.sample_odd_from_range","title":"def sample_odd_from_range (random_state, low, high) [view source on GitHub]","text":"

Sample an odd number from the range [low, high] (inclusive).

Parameters:

Name Type Description random_state Random

instance of random.Random

low int

lower bound (will be converted to nearest valid odd number)

high int

upper bound (will be converted to nearest valid odd number)

Returns:

Type Description int

Randomly sampled odd number from the range

Note

  • Input values will be converted to nearest valid odd numbers:
  • Values less than 3 will become 3
  • Even values will be rounded up to next odd number
  • After normalization, high must be >= low
Source code in albumentations/augmentations/blur/functional.py Python
def sample_odd_from_range(random_state: Random, low: int, high: int) -> int:\n    \"\"\"Sample an odd number from the range [low, high] (inclusive).\n\n    Args:\n        random_state: instance of random.Random\n        low: lower bound (will be converted to nearest valid odd number)\n        high: upper bound (will be converted to nearest valid odd number)\n\n    Returns:\n        Randomly sampled odd number from the range\n\n    Note:\n        - Input values will be converted to nearest valid odd numbers:\n          * Values less than 3 will become 3\n          * Even values will be rounded up to next odd number\n        - After normalization, high must be >= low\n    \"\"\"\n    # Normalize low value\n    low = max(3, low + (low % 2 == 0))\n    # Normalize high value\n    high = max(3, high + (high % 2 == 0))\n\n    # Ensure high >= low after normalization\n    high = max(high, low)\n\n    if low == high:\n        return low\n\n    # Calculate number of possible odd values\n    num_odd_values = (high - low) // 2 + 1\n    # Generate random index and convert to corresponding odd number\n    rand_idx = random_state.randint(0, num_odd_values - 1)\n    return low + (2 * rand_idx)\n
"},{"location":"api_reference/augmentations/blur/transforms/","title":"Blur transforms (augmentations.blur.transforms)","text":""},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.AdvancedBlur","title":"class AdvancedBlur (blur_limit=(3, 7), sigma_x_limit=(0.2, 1.0), sigma_y_limit=(0.2, 1.0), sigmaX_limit=None, sigmaY_limit=None, rotate_limit=(-90, 90), beta_limit=(0.5, 8.0), noise_limit=(0.9, 1.1), always_apply=None, p=0.5) [view source on GitHub]","text":"

Applies a Generalized Gaussian blur to the input image with randomized parameters for advanced data augmentation.

This transform creates a custom blur kernel based on the Generalized Gaussian distribution, which allows for a wide range of blur effects beyond standard Gaussian blur. It then applies this kernel to the input image through convolution. The transform also incorporates noise into the kernel, resulting in a unique combination of blurring and noise injection.

Key features of this augmentation:

  1. Generalized Gaussian Kernel: Uses a generalized normal distribution to create kernels that can range from box-like blurs to very peaked blurs, controlled by the beta parameter.

  2. Anisotropic Blurring: Allows for different blur strengths in horizontal and vertical directions (controlled by sigma_x and sigma_y), and rotation of the kernel.

  3. Kernel Noise: Adds multiplicative noise to the kernel before applying it to the image, creating more diverse and realistic blur effects.

Implementation Details: The kernel is generated using a 2D Generalized Gaussian function. The process involves: 1. Creating a 2D grid based on the kernel size 2. Applying rotation to this grid 3. Calculating the kernel values using the Generalized Gaussian formula 4. Adding multiplicative noise to the kernel 5. Normalizing the kernel

The resulting kernel is then applied to the image using convolution.\n

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the size of the blur kernel. If a single int is provided, the kernel size will be randomly chosen between 3 and that value. Must be odd and \u2265 3. Larger values create stronger blur effects. Default: (3, 7)

sigma_x_limit tuple[float, float] | float

Controls the spread of the blur in the x direction. Higher values increase blur strength. If a single float is provided, the range will be (0, limit). Default: (0.2, 1.0)

sigma_y_limit tuple[float, float] | float

Controls the spread of the blur in the y direction. Higher values increase blur strength. If a single float is provided, the range will be (0, limit). Default: (0.2, 1.0)

rotate_limit tuple[int, int] | int

Range of angles (in degrees) for rotating the kernel. This rotation allows for diagonal blur directions. If limit is a single int, an angle is picked from (-rotate_limit, rotate_limit). Default: (-90, 90)

beta_limit tuple[float, float] | float

Shape parameter of the Generalized Gaussian distribution. - beta = 1 gives a standard Gaussian distribution - beta < 1 creates heavier tails, resulting in more uniform, box-like blur - beta > 1 creates lighter tails, resulting in more peaked, focused blur Default: (0.5, 8.0)

noise_limit tuple[float, float] | float

Controls the strength of multiplicative noise applied to the kernel. Values around 1.0 keep the original kernel mostly intact, while values further from 1.0 introduce more variation. Default: (0.75, 1.25)

p float

Probability of applying the transform. Default: 0.5

Notes

  • This transform is particularly useful for simulating complex, real-world blur effects that go beyond simple Gaussian blur.
  • The combination of blur and noise can help in creating more robust models by simulating a wider range of image degradations.
  • Extreme values, especially for beta and noise, may result in unrealistic effects and should be used cautiously.

Reference

This transform is inspired by techniques described in: \"Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data\" https://arxiv.org/abs/2107.10833

Targets

image

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class AdvancedBlur(ImageOnlyTransform):\n    \"\"\"Applies a Generalized Gaussian blur to the input image with randomized parameters for advanced data augmentation.\n\n    This transform creates a custom blur kernel based on the Generalized Gaussian distribution,\n    which allows for a wide range of blur effects beyond standard Gaussian blur. It then applies\n    this kernel to the input image through convolution. The transform also incorporates noise\n    into the kernel, resulting in a unique combination of blurring and noise injection.\n\n    Key features of this augmentation:\n\n    1. Generalized Gaussian Kernel: Uses a generalized normal distribution to create kernels\n       that can range from box-like blurs to very peaked blurs, controlled by the beta parameter.\n\n    2. Anisotropic Blurring: Allows for different blur strengths in horizontal and vertical\n       directions (controlled by sigma_x and sigma_y), and rotation of the kernel.\n\n    3. Kernel Noise: Adds multiplicative noise to the kernel before applying it to the image,\n       creating more diverse and realistic blur effects.\n\n    Implementation Details:\n        The kernel is generated using a 2D Generalized Gaussian function. The process involves:\n        1. Creating a 2D grid based on the kernel size\n        2. Applying rotation to this grid\n        3. Calculating the kernel values using the Generalized Gaussian formula\n        4. Adding multiplicative noise to the kernel\n        5. Normalizing the kernel\n\n        The resulting kernel is then applied to the image using convolution.\n\n    Args:\n        blur_limit (tuple[int, int] | int, optional): Controls the size of the blur kernel. If a single int\n            is provided, the kernel size will be randomly chosen between 3 and that value.\n            Must be odd and \u2265 3. Larger values create stronger blur effects.\n            Default: (3, 7)\n\n        sigma_x_limit (tuple[float, float] | float): Controls the spread of the blur in the x direction.\n            Higher values increase blur strength.\n            If a single float is provided, the range will be (0, limit).\n            Default: (0.2, 1.0)\n\n        sigma_y_limit (tuple[float, float] | float): Controls the spread of the blur in the y direction.\n            Higher values increase blur strength.\n            If a single float is provided, the range will be (0, limit).\n            Default: (0.2, 1.0)\n\n        rotate_limit (tuple[int, int] | int): Range of angles (in degrees) for rotating the kernel.\n            This rotation allows for diagonal blur directions. If limit is a single int, an angle is picked\n            from (-rotate_limit, rotate_limit).\n            Default: (-90, 90)\n\n        beta_limit (tuple[float, float] | float): Shape parameter of the Generalized Gaussian distribution.\n            - beta = 1 gives a standard Gaussian distribution\n            - beta < 1 creates heavier tails, resulting in more uniform, box-like blur\n            - beta > 1 creates lighter tails, resulting in more peaked, focused blur\n            Default: (0.5, 8.0)\n\n        noise_limit (tuple[float, float] | float): Controls the strength of multiplicative noise\n            applied to the kernel. Values around 1.0 keep the original kernel mostly intact,\n            while values further from 1.0 introduce more variation.\n            Default: (0.75, 1.25)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - This transform is particularly useful for simulating complex, real-world blur effects\n          that go beyond simple Gaussian blur.\n        - The combination of blur and noise can help in creating more robust models by simulating\n          a wider range of image degradations.\n        - Extreme values, especially for beta and noise, may result in unrealistic effects and\n          should be used cautiously.\n\n    Reference:\n        This transform is inspired by techniques described in:\n        \"Real-ESRGAN: Training Real-World Blind Super-Resolution with Pure Synthetic Data\"\n        https://arxiv.org/abs/2107.10833\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        sigma_x_limit: NonNegativeFloatRangeType\n        sigma_y_limit: NonNegativeFloatRangeType\n        beta_limit: NonNegativeFloatRangeType\n        noise_limit: NonNegativeFloatRangeType\n        rotate_limit: SymmetricRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_beta_limit(cls, value: ScaleFloatType) -> tuple[float, float]:\n            result = to_tuple(value, low=0)\n            if not (result[0] < 1.0 < result[1]):\n                msg = \"beta_limit is expected to include 1.0.\"\n                raise ValueError(msg)\n            return result\n\n        @model_validator(mode=\"after\")\n        def validate_limits(self) -> Self:\n            if (\n                isinstance(self.sigma_x_limit, (tuple, list))\n                and self.sigma_x_limit[0] == 0\n                and isinstance(self.sigma_y_limit, (tuple, list))\n                and self.sigma_y_limit[0] == 0\n            ):\n                msg = \"sigma_x_limit and sigma_y_limit minimum value cannot be both equal to 0.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_x_limit: ScaleFloatType = (0.2, 1.0),\n        sigma_y_limit: ScaleFloatType = (0.2, 1.0),\n        sigmaX_limit: ScaleFloatType | None = None,  # noqa: N803\n        sigmaY_limit: ScaleFloatType | None = None,  # noqa: N803\n        rotate_limit: ScaleIntType = (-90, 90),\n        beta_limit: ScaleFloatType = (0.5, 8.0),\n        noise_limit: ScaleFloatType = (0.9, 1.1),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        if sigmaX_limit is not None:\n            warnings.warn(\n                \"sigmaX_limit is deprecated; use sigma_x_limit instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            sigma_x_limit = sigmaX_limit\n\n        if sigmaY_limit is not None:\n            warnings.warn(\n                \"sigmaY_limit is deprecated; use sigma_y_limit instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            sigma_y_limit = sigmaY_limit\n\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_x_limit = cast(tuple[float, float], sigma_x_limit)\n        self.sigma_y_limit = cast(tuple[float, float], sigma_y_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n        self.noise_limit = cast(tuple[float, float], noise_limit)\n\n    def apply(self, img: np.ndarray, kernel: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel=kernel)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n        sigma_x = self.py_random.uniform(*self.sigma_x_limit)\n        sigma_y = self.py_random.uniform(*self.sigma_y_limit)\n        angle = np.deg2rad(self.py_random.uniform(*self.rotate_limit))\n\n        # Split into 2 cases to avoid selection of narrow kernels (beta > 1) too often.\n        beta = (\n            self.py_random.uniform(self.beta_limit[0], 1)\n            if self.py_random.random() < HALF\n            else self.py_random.uniform(1, self.beta_limit[1])\n        )\n\n        noise_matrix = self.random_generator.uniform(\n            *self.noise_limit,\n            size=(ksize, ksize),\n        )\n\n        # Generate mesh grid centered at zero.\n        ax = np.arange(-ksize // 2 + 1.0, ksize // 2 + 1.0)\n        # > Shape (ksize, ksize, 2)\n        grid = np.stack(np.meshgrid(ax, ax), axis=-1)\n\n        # Calculate rotated sigma matrix\n        d_matrix = np.array([[sigma_x**2, 0], [0, sigma_y**2]])\n        u_matrix = np.array(\n            [[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]],\n        )\n        sigma_matrix = np.dot(u_matrix, np.dot(d_matrix, u_matrix.T))\n\n        inverse_sigma = np.linalg.inv(sigma_matrix)\n        # Described in \"Parameter Estimation For Multivariate Generalized Gaussian Distributions\"\n        kernel = np.exp(\n            -0.5 * np.power(np.sum(np.dot(grid, inverse_sigma) * grid, 2), beta),\n        )\n        # Add noise\n        kernel *= noise_matrix\n\n        # Normalize kernel\n        kernel = kernel.astype(np.float32) / np.sum(kernel)\n        return {\"kernel\": kernel}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str, str, str]:\n        return (\n            \"blur_limit\",\n            \"sigma_x_limit\",\n            \"sigma_y_limit\",\n            \"rotate_limit\",\n            \"beta_limit\",\n            \"noise_limit\",\n        )\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.Blur","title":"class Blur (blur_limit=(3, 7), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply uniform box blur to the input image using a randomly sized square kernel.

This transform uses OpenCV's cv2.blur function, which performs a simple box filter blur. The size of the blur kernel is randomly selected for each application, allowing for varying degrees of blur intensity.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the range of the blur kernel size. - If a single int is provided, the kernel size will be randomly chosen between 3 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. The kernel size must be odd and greater than or equal to 3. Larger kernel sizes produce stronger blur effects. Default: (3, 7)

p float

Probability of applying the transform. Default: 0.5

Notes

  • The blur kernel is always square (same width and height).
  • Only odd kernel sizes are used to ensure the blur has a clear center pixel.
  • Box blur is faster than Gaussian blur but may produce less natural results.
  • This blur method averages all pixels under the kernel area, which can reduce noise but also reduce image detail.

Targets

image

Image types: uint8, float32

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Blur(blur_limit=(3, 7), p=1.0)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class Blur(ImageOnlyTransform):\n    \"\"\"Apply uniform box blur to the input image using a randomly sized square kernel.\n\n    This transform uses OpenCV's cv2.blur function, which performs a simple box filter blur.\n    The size of the blur kernel is randomly selected for each application, allowing for\n    varying degrees of blur intensity.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Controls the range of the blur kernel size.\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 3 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            The kernel size must be odd and greater than or equal to 3.\n            Larger kernel sizes produce stronger blur effects.\n            Default: (3, 7)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Notes:\n        - The blur kernel is always square (same width and height).\n        - Only odd kernel sizes are used to ensure the blur has a clear center pixel.\n        - Box blur is faster than Gaussian blur but may produce less natural results.\n        - This blur method averages all pixels under the kernel area, which can\n          reduce noise but also reduce image detail.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Blur(blur_limit=(3, 7), p=1.0)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        pass\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fblur.blur(img, kernel)\n\n    def get_params(self) -> dict[str, Any]:\n        kernel = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n        return {\"kernel\": kernel}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"blur_limit\",)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.BlurInitSchema","title":"class BlurInitSchema [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class BlurInitSchema(BaseTransformInitSchema):\n    blur_limit: ScaleIntType\n\n    @field_validator(\"blur_limit\")\n    @classmethod\n    def process_blur(cls, value: ScaleIntType, info: ValidationInfo) -> tuple[int, int]:\n        return fblur.process_blur_limit(value, info, min_value=3)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.Defocus","title":"class Defocus (radius=(3, 10), alias_blur=(0.1, 0.5), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply defocus blur to the input image.

This transform simulates the effect of an out-of-focus camera by applying a defocus blur to the image. It uses a combination of disc kernels and Gaussian blur to create a realistic defocus effect.

Parameters:

Name Type Description radius tuple[int, int] | int

Range for the radius of the defocus blur. If a single int is provided, the range will be [1, radius]. Larger values create a stronger blur effect. Default: (3, 10)

alias_blur tuple[float, float] | float

Range for the standard deviation of the Gaussian blur applied after the main defocus blur. This helps to reduce aliasing artifacts. If a single float is provided, the range will be (0, alias_blur). Larger values create a smoother, more aliased effect. Default: (0.1, 0.5)

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • The defocus effect is created using a disc kernel, which simulates the shape of a camera's aperture.
  • The additional Gaussian blur (alias_blur) helps to soften the edges of the disc kernel, creating a more natural-looking defocus effect.
  • Larger radius values will create a stronger, more noticeable defocus effect.
  • The alias_blur parameter can be used to fine-tune the appearance of the defocus, with larger values creating a smoother, potentially more realistic effect.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Defocus(radius=(4, 8), alias_blur=(0.2, 0.4), always_apply=True)\n>>> result = transform(image=image)\n>>> defocused_image = result['image']\n

References

  • https://en.wikipedia.org/wiki/Defocus_aberration
  • https://www.researchgate.net/publication/261311609_Realistic_Defocus_Blur_for_Multiplane_Computer-Generated_Holography

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class Defocus(ImageOnlyTransform):\n    \"\"\"Apply defocus blur to the input image.\n\n    This transform simulates the effect of an out-of-focus camera by applying a defocus blur\n    to the image. It uses a combination of disc kernels and Gaussian blur to create a realistic\n    defocus effect.\n\n    Args:\n        radius (tuple[int, int] | int): Range for the radius of the defocus blur.\n            If a single int is provided, the range will be [1, radius].\n            Larger values create a stronger blur effect.\n            Default: (3, 10)\n\n        alias_blur (tuple[float, float] | float): Range for the standard deviation of the Gaussian blur\n            applied after the main defocus blur. This helps to reduce aliasing artifacts.\n            If a single float is provided, the range will be (0, alias_blur).\n            Larger values create a smoother, more aliased effect.\n            Default: (0.1, 0.5)\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The defocus effect is created using a disc kernel, which simulates the shape of a camera's aperture.\n        - The additional Gaussian blur (alias_blur) helps to soften the edges of the disc kernel, creating a\n          more natural-looking defocus effect.\n        - Larger radius values will create a stronger, more noticeable defocus effect.\n        - The alias_blur parameter can be used to fine-tune the appearance of the defocus, with larger values\n          creating a smoother, potentially more realistic effect.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Defocus(radius=(4, 8), alias_blur=(0.2, 0.4), always_apply=True)\n        >>> result = transform(image=image)\n        >>> defocused_image = result['image']\n\n    References:\n        - https://en.wikipedia.org/wiki/Defocus_aberration\n        - https://www.researchgate.net/publication/261311609_Realistic_Defocus_Blur_for_Multiplane_Computer-Generated_Holography\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        radius: OnePlusIntRangeType\n        alias_blur: NonNegativeFloatRangeType\n\n    def __init__(\n        self,\n        radius: ScaleIntType = (3, 10),\n        alias_blur: ScaleFloatType = (0.1, 0.5),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.radius = cast(tuple[int, int], radius)\n        self.alias_blur = cast(tuple[float, float], alias_blur)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        radius: int,\n        alias_blur: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.defocus(img, radius, alias_blur)\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"radius\": self.py_random.randint(*self.radius),\n            \"alias_blur\": self.py_random.uniform(*self.alias_blur),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"radius\", \"alias_blur\")\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.GaussianBlur","title":"class GaussianBlur (blur_limit=(3, 7), sigma_limit=0, always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply Gaussian blur to the input image using a randomly sized kernel.

This transform blurs the input image using a Gaussian filter with a random kernel size and sigma value. Gaussian blur is a widely used image processing technique that reduces image noise and detail, creating a smoothing effect.

Parameters:

Name Type Description blur_limit tuple[int, int] | int

Controls the range of the Gaussian kernel size. - If a single int is provided, the kernel size will be randomly chosen between 0 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. Must be zero or odd and in range [0, inf). If set to 0, it will be computed from sigma as round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1. Larger kernel sizes produce stronger blur effects. Default: (3, 7)

sigma_limit tuple[float, float] | float

Range for the Gaussian kernel standard deviation (sigma). Must be in range [0, inf). - If a single float is provided, sigma will be randomly chosen between 0 and that value. - If a tuple of two floats is provided, it defines the inclusive range of possible sigma values. If set to 0, sigma will be computed as sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8. Larger sigma values produce stronger blur effects. Default: 0

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The relationship between kernel size and sigma affects the blur strength: larger kernel sizes allow for stronger blurring effects.
  • When both blur_limit and sigma_limit are set to ranges starting from 0, the blur_limit minimum is automatically set to 3 to ensure a valid kernel size.
  • For uint8 images, the computation might be faster than for floating-point images.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.GaussianBlur(blur_limit=(3, 7), sigma_limit=(0.1, 2), p=1)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class GaussianBlur(ImageOnlyTransform):\n    \"\"\"Apply Gaussian blur to the input image using a randomly sized kernel.\n\n    This transform blurs the input image using a Gaussian filter with a random kernel size\n    and sigma value. Gaussian blur is a widely used image processing technique that reduces\n    image noise and detail, creating a smoothing effect.\n\n    Args:\n        blur_limit (tuple[int, int] | int): Controls the range of the Gaussian kernel size.\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 0 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            Must be zero or odd and in range [0, inf). If set to 0, it will be computed\n            from sigma as `round(sigma * (3 if img.dtype == np.uint8 else 4) * 2 + 1) + 1`.\n            Larger kernel sizes produce stronger blur effects.\n            Default: (3, 7)\n\n        sigma_limit (tuple[float, float] | float): Range for the Gaussian kernel standard\n            deviation (sigma). Must be in range [0, inf).\n            - If a single float is provided, sigma will be randomly chosen\n              between 0 and that value.\n            - If a tuple of two floats is provided, it defines the inclusive range\n              of possible sigma values.\n            If set to 0, sigma will be computed as `sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8`.\n            Larger sigma values produce stronger blur effects.\n            Default: 0\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The relationship between kernel size and sigma affects the blur strength:\n          larger kernel sizes allow for stronger blurring effects.\n        - When both blur_limit and sigma_limit are set to ranges starting from 0,\n          the blur_limit minimum is automatically set to 3 to ensure a valid kernel size.\n        - For uint8 images, the computation might be faster than for floating-point images.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.GaussianBlur(blur_limit=(3, 7), sigma_limit=(0.1, 2), p=1)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        sigma_limit: NonNegativeFloatRangeType\n\n        @field_validator(\"blur_limit\")\n        @classmethod\n        def process_blur(\n            cls,\n            value: ScaleIntType,\n            info: ValidationInfo,\n        ) -> tuple[int, int]:\n            return fblur.process_blur_limit(value, info, min_value=0)\n\n        @model_validator(mode=\"after\")\n        def validate_limits(self) -> Self:\n            if (\n                isinstance(self.blur_limit, (tuple, list))\n                and self.blur_limit[0] == 0\n                and isinstance(self.sigma_limit, (tuple, list))\n                and self.sigma_limit[0] == 0\n            ):\n                self.blur_limit = 3, max(3, self.blur_limit[1])\n                warnings.warn(\n                    \"blur_limit and sigma_limit minimum value can not be both equal to 0. \"\n                    \"blur_limit minimum value changed to 3.\",\n                    stacklevel=2,\n                )\n\n            return self\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = (3, 7),\n        sigma_limit: ScaleFloatType = 0,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p, always_apply)\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.sigma_limit = cast(tuple[float, float], sigma_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        ksize: int,\n        sigma: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.gaussian_blur(img, ksize, sigma=sigma)\n\n    def get_params(self) -> dict[str, float]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n\n        return {\"ksize\": ksize, \"sigma\": self.py_random.uniform(*self.sigma_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"blur_limit\", \"sigma_limit\"\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.GlassBlur","title":"class GlassBlur (sigma=0.7, max_delta=4, iterations=2, mode='fast', always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply a glass blur effect to the input image.

This transform simulates the effect of looking through textured glass by locally shuffling pixels in the image. It creates a distorted, frosted glass-like appearance.

Parameters:

Name Type Description sigma float

Standard deviation for the Gaussian kernel used in the process. Higher values increase the blur effect. Must be non-negative. Default: 0.7

max_delta int

Maximum distance in pixels for shuffling. Determines how far pixels can be moved. Larger values create more distortion. Must be a positive integer. Default: 4

iterations int

Number of times to apply the glass blur effect. More iterations create a stronger effect but increase computation time. Must be a positive integer. Default: 2

mode Literal[\"fast\", \"exact\"]

Mode of computation. Options are: - \"fast\": Uses a faster but potentially less accurate method. - \"exact\": Uses a slower but more precise method. Default: \"fast\"

p float

Probability of applying the transform. Should be in the range [0, 1]. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • This transform is particularly effective for creating a 'looking through glass' effect or simulating the view through a frosted window.
  • The 'fast' mode is recommended for most use cases as it provides a good balance between effect quality and computation speed.
  • Increasing 'iterations' will strengthen the effect but also increase the processing time linearly.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.GlassBlur(sigma=0.7, max_delta=4, iterations=3, mode=\"fast\", p=1)\n>>> result = transform(image=image)\n>>> glass_blurred_image = result[\"image\"]\n

References

  • This implementation is based on the technique described in: \"ImageNet-trained CNNs are biased towards texture; increasing shape bias improves accuracy and robustness\" https://arxiv.org/abs/1903.12261
  • Original implementation: https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class GlassBlur(ImageOnlyTransform):\n    \"\"\"Apply a glass blur effect to the input image.\n\n    This transform simulates the effect of looking through textured glass by locally\n    shuffling pixels in the image. It creates a distorted, frosted glass-like appearance.\n\n    Args:\n        sigma (float): Standard deviation for the Gaussian kernel used in the process.\n            Higher values increase the blur effect. Must be non-negative.\n            Default: 0.7\n\n        max_delta (int): Maximum distance in pixels for shuffling.\n            Determines how far pixels can be moved. Larger values create more distortion.\n            Must be a positive integer.\n            Default: 4\n\n        iterations (int): Number of times to apply the glass blur effect.\n            More iterations create a stronger effect but increase computation time.\n            Must be a positive integer.\n            Default: 2\n\n        mode (Literal[\"fast\", \"exact\"]): Mode of computation. Options are:\n            - \"fast\": Uses a faster but potentially less accurate method.\n            - \"exact\": Uses a slower but more precise method.\n            Default: \"fast\"\n\n        p (float): Probability of applying the transform. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - This transform is particularly effective for creating a 'looking through\n          glass' effect or simulating the view through a frosted window.\n        - The 'fast' mode is recommended for most use cases as it provides a good\n          balance between effect quality and computation speed.\n        - Increasing 'iterations' will strengthen the effect but also increase the\n          processing time linearly.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.GlassBlur(sigma=0.7, max_delta=4, iterations=3, mode=\"fast\", p=1)\n        >>> result = transform(image=image)\n        >>> glass_blurred_image = result[\"image\"]\n\n    References:\n        - This implementation is based on the technique described in:\n          \"ImageNet-trained CNNs are biased towards texture; increasing shape bias improves accuracy and robustness\"\n          https://arxiv.org/abs/1903.12261\n        - Original implementation:\n          https://github.com/hendrycks/robustness/blob/master/ImageNet-C/create_c/make_imagenet_c.py\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        sigma: float = Field(ge=0)\n        max_delta: int = Field(ge=1)\n        iterations: int = Field(ge=1)\n        mode: Literal[\"fast\", \"exact\"]\n\n    def __init__(\n        self,\n        sigma: float = 0.7,\n        max_delta: int = 4,\n        iterations: int = 2,\n        mode: Literal[\"fast\", \"exact\"] = \"fast\",\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.sigma = sigma\n        self.max_delta = max_delta\n        self.iterations = iterations\n        self.mode = mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        *args: Any,\n        dxy: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.glass_blur(\n            img,\n            self.sigma,\n            self.max_delta,\n            self.iterations,\n            dxy,\n            self.mode,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n\n        # generate array containing all necessary values for transformations\n        width_pixels = height - self.max_delta * 2\n        height_pixels = width - self.max_delta * 2\n        total_pixels = int(width_pixels * height_pixels)\n        dxy = self.random_generator.integers(\n            -self.max_delta,\n            self.max_delta,\n            size=(total_pixels, self.iterations, 2),\n        )\n\n        return {\"dxy\": dxy}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"sigma\", \"max_delta\", \"iterations\", \"mode\"\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.MedianBlur","title":"class MedianBlur (blur_limit=7, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply median blur to the input image.

This transform uses a median filter to blur the input image. Median filtering is particularly effective at removing salt-and-pepper noise while preserving edges, making it a popular choice for noise reduction in image processing.

Parameters:

Name Type Description blur_limit int | tuple[int, int]

Maximum aperture linear size for blurring the input image. Must be odd and in the range [3, inf). - If a single int is provided, the kernel size will be randomly chosen between 3 and that value. - If a tuple of two ints is provided, it defines the inclusive range of possible kernel sizes. Default: (3, 7)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The kernel size (aperture linear size) must always be odd and greater than 1.
  • Unlike mean blur or Gaussian blur, median blur uses the median of all pixels under the kernel area, making it more robust to outliers.
  • This transform is particularly useful for:
  • Removing salt-and-pepper noise
  • Preserving edges while smoothing images
  • Pre-processing images for edge detection algorithms
  • For color images, the median is calculated independently for each channel.
  • Larger kernel sizes result in stronger blurring effects but may also remove fine details from the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.MedianBlur(blur_limit=(3, 7), p=0.5)\n>>> result = transform(image=image)\n>>> blurred_image = result[\"image\"]\n

References

  • Median filter: https://en.wikipedia.org/wiki/Median_filter
  • OpenCV medianBlur: https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class MedianBlur(Blur):\n    \"\"\"Apply median blur to the input image.\n\n    This transform uses a median filter to blur the input image. Median filtering is particularly\n    effective at removing salt-and-pepper noise while preserving edges, making it a popular choice\n    for noise reduction in image processing.\n\n    Args:\n        blur_limit (int | tuple[int, int]): Maximum aperture linear size for blurring the input image.\n            Must be odd and in the range [3, inf).\n            - If a single int is provided, the kernel size will be randomly chosen\n              between 3 and that value.\n            - If a tuple of two ints is provided, it defines the inclusive range\n              of possible kernel sizes.\n            Default: (3, 7)\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The kernel size (aperture linear size) must always be odd and greater than 1.\n        - Unlike mean blur or Gaussian blur, median blur uses the median of all pixels under\n          the kernel area, making it more robust to outliers.\n        - This transform is particularly useful for:\n          * Removing salt-and-pepper noise\n          * Preserving edges while smoothing images\n          * Pre-processing images for edge detection algorithms\n        - For color images, the median is calculated independently for each channel.\n        - Larger kernel sizes result in stronger blurring effects but may also remove\n          fine details from the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.MedianBlur(blur_limit=(3, 7), p=0.5)\n        >>> result = transform(image=image)\n        >>> blurred_image = result[\"image\"]\n\n    References:\n        - Median filter: https://en.wikipedia.org/wiki/Median_filter\n        - OpenCV medianBlur: https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9\n    \"\"\"\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = 7,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(blur_limit=blur_limit, p=p, always_apply=always_apply)\n\n    def apply(self, img: np.ndarray, kernel: int, **params: Any) -> np.ndarray:\n        return fblur.median_blur(img, kernel)\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.MotionBlur","title":"class MotionBlur (blur_limit=7, allow_shifted=True, angle_range=(0, 360), direction_range=(-1.0, 1.0), p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply motion blur to the input image using a directional kernel.

This transform simulates motion blur effects that occur during image capture, such as camera shake or object movement. It creates a directional blur using a line-shaped kernel with controllable angle, direction, and position.

Parameters:

Name Type Description blur_limit int | tuple[int, int]

Maximum kernel size for blurring. Should be in range [3, inf). - If int: kernel size will be randomly chosen from [3, blur_limit] - If tuple: kernel size will be randomly chosen from [min, max] Larger values create stronger blur effects. Default: (3, 7)

angle_range tuple[float, float]

Range of possible angles in degrees. Controls the rotation of the motion blur line: - 0\u00b0: Horizontal motion blur \u2192 - 45\u00b0: Diagonal motion blur \u2197 - 90\u00b0: Vertical motion blur \u2191 - 135\u00b0: Diagonal motion blur \u2196 Default: (0, 360)

direction_range tuple[float, float]

Range for motion bias. Controls how the blur extends from the center: - -1.0: Blur extends only backward (\u2190) - 0.0: Blur extends equally in both directions (\u2190\u2192) - 1.0: Blur extends only forward (\u2192) For example, with angle=0: - direction=-1.0: \u2190\u2022 - direction=0.0: \u2190\u2022\u2192 - direction=1.0: \u2022\u2192 Default: (-1.0, 1.0)

allow_shifted bool

Allow random kernel position shifts. - If True: Kernel can be randomly offset from center - If False: Kernel will always be centered Default: True

p float

Probability of applying the transform. Default: 0.5

Examples of angle vs direction: 1. Horizontal motion (angle=0\u00b0): - direction=0.0: \u2190\u2022\u2192 (symmetric blur) - direction=1.0: \u2022\u2192 (forward blur) - direction=-1.0: \u2190\u2022 (backward blur)

2. Vertical motion (angle=90\u00b0):\n   - direction=0.0:   \u2191\u2022\u2193   (symmetric blur)\n   - direction=1.0:    \u2022\u2191   (upward blur)\n   - direction=-1.0:  \u2193\u2022    (downward blur)\n\n3. Diagonal motion (angle=45\u00b0):\n   - direction=0.0:   \u2199\u2022\u2197   (symmetric blur)\n   - direction=1.0:    \u2022\u2197   (forward diagonal blur)\n   - direction=-1.0:  \u2199\u2022    (backward diagonal blur)\n

Note

  • angle controls the orientation of the motion line
  • direction controls the distribution of the blur along that line
  • Together they can simulate various motion effects:
  • Camera shake: Small angle range + direction near 0
  • Object motion: Specific angle + direction=1.0
  • Complex motion: Random angle + random direction

Examples:

Python
>>> import albumentations as A\n>>> # Horizontal camera shake (symmetric)\n>>> transform = A.MotionBlur(\n...     angle_range=(-5, 5),      # Near-horizontal motion\n...     direction_range=(0, 0),    # Symmetric blur\n...     p=1.0\n... )\n>>>\n>>> # Object moving right\n>>> transform = A.MotionBlur(\n...     angle_range=(0, 0),        # Horizontal motion\n...     direction_range=(0.8, 1.0), # Strong forward bias\n...     p=1.0\n... )\n

References

  • Motion blur fundamentals: https://en.wikipedia.org/wiki/Motion_blur

  • Directional blur kernels: https://www.sciencedirect.com/topics/computer-science/directional-blur

  • OpenCV filter2D (used for convolution): https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04

  • Research on motion blur simulation: \"Understanding and Evaluating Blind Deconvolution Algorithms\" (CVPR 2009) https://doi.org/10.1109/CVPR.2009.5206815

  • Motion blur in photography: \"The Manual of Photography\", Chapter 7: Motion in Photography ISBN: 978-0240520377

  • Kornia's implementation (similar approach): https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomMotionBlur

See Also: - GaussianBlur: For uniform blur effects - MedianBlur: For noise reduction while preserving edges - RandomRain: Another motion-based effect - Perspective: For geometric motion-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class MotionBlur(Blur):\n    \"\"\"Apply motion blur to the input image using a directional kernel.\n\n    This transform simulates motion blur effects that occur during image capture,\n    such as camera shake or object movement. It creates a directional blur using\n    a line-shaped kernel with controllable angle, direction, and position.\n\n    Args:\n        blur_limit (int | tuple[int, int]): Maximum kernel size for blurring.\n            Should be in range [3, inf).\n            - If int: kernel size will be randomly chosen from [3, blur_limit]\n            - If tuple: kernel size will be randomly chosen from [min, max]\n            Larger values create stronger blur effects.\n            Default: (3, 7)\n\n        angle_range (tuple[float, float]): Range of possible angles in degrees.\n            Controls the rotation of the motion blur line:\n            - 0\u00b0: Horizontal motion blur \u2192\n            - 45\u00b0: Diagonal motion blur \u2197\n            - 90\u00b0: Vertical motion blur \u2191\n            - 135\u00b0: Diagonal motion blur \u2196\n            Default: (0, 360)\n\n        direction_range (tuple[float, float]): Range for motion bias.\n            Controls how the blur extends from the center:\n            - -1.0: Blur extends only backward (\u2190)\n            -  0.0: Blur extends equally in both directions (\u2190\u2192)\n            -  1.0: Blur extends only forward (\u2192)\n            For example, with angle=0:\n            - direction=-1.0: \u2190\u2022\n            - direction=0.0:  \u2190\u2022\u2192\n            - direction=1.0:   \u2022\u2192\n            Default: (-1.0, 1.0)\n\n        allow_shifted (bool): Allow random kernel position shifts.\n            - If True: Kernel can be randomly offset from center\n            - If False: Kernel will always be centered\n            Default: True\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Examples of angle vs direction:\n        1. Horizontal motion (angle=0\u00b0):\n           - direction=0.0:   \u2190\u2022\u2192   (symmetric blur)\n           - direction=1.0:    \u2022\u2192   (forward blur)\n           - direction=-1.0:  \u2190\u2022    (backward blur)\n\n        2. Vertical motion (angle=90\u00b0):\n           - direction=0.0:   \u2191\u2022\u2193   (symmetric blur)\n           - direction=1.0:    \u2022\u2191   (upward blur)\n           - direction=-1.0:  \u2193\u2022    (downward blur)\n\n        3. Diagonal motion (angle=45\u00b0):\n           - direction=0.0:   \u2199\u2022\u2197   (symmetric blur)\n           - direction=1.0:    \u2022\u2197   (forward diagonal blur)\n           - direction=-1.0:  \u2199\u2022    (backward diagonal blur)\n\n    Note:\n        - angle controls the orientation of the motion line\n        - direction controls the distribution of the blur along that line\n        - Together they can simulate various motion effects:\n          * Camera shake: Small angle range + direction near 0\n          * Object motion: Specific angle + direction=1.0\n          * Complex motion: Random angle + random direction\n\n    Example:\n        >>> import albumentations as A\n        >>> # Horizontal camera shake (symmetric)\n        >>> transform = A.MotionBlur(\n        ...     angle_range=(-5, 5),      # Near-horizontal motion\n        ...     direction_range=(0, 0),    # Symmetric blur\n        ...     p=1.0\n        ... )\n        >>>\n        >>> # Object moving right\n        >>> transform = A.MotionBlur(\n        ...     angle_range=(0, 0),        # Horizontal motion\n        ...     direction_range=(0.8, 1.0), # Strong forward bias\n        ...     p=1.0\n        ... )\n\n    References:\n        - Motion blur fundamentals:\n          https://en.wikipedia.org/wiki/Motion_blur\n\n        - Directional blur kernels:\n          https://www.sciencedirect.com/topics/computer-science/directional-blur\n\n        - OpenCV filter2D (used for convolution):\n          https://docs.opencv.org/master/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04\n\n        - Research on motion blur simulation:\n          \"Understanding and Evaluating Blind Deconvolution Algorithms\" (CVPR 2009)\n          https://doi.org/10.1109/CVPR.2009.5206815\n\n        - Motion blur in photography:\n          \"The Manual of Photography\", Chapter 7: Motion in Photography\n          ISBN: 978-0240520377\n\n        - Kornia's implementation (similar approach):\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomMotionBlur\n\n    See Also:\n        - GaussianBlur: For uniform blur effects\n        - MedianBlur: For noise reduction while preserving edges\n        - RandomRain: Another motion-based effect\n        - Perspective: For geometric motion-like distortions\n\n    \"\"\"\n\n    class InitSchema(BlurInitSchema):\n        allow_shifted: bool\n        angle_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 360)),\n        ]\n        direction_range: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(min_val=-1.0, max_val=1.0)),\n        ]\n\n    def __init__(\n        self,\n        blur_limit: ScaleIntType = 7,\n        allow_shifted: bool = True,\n        angle_range: tuple[float, float] = (0, 360),\n        direction_range: tuple[float, float] = (-1.0, 1.0),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(blur_limit=blur_limit, p=p)\n        self.allow_shifted = allow_shifted\n        self.blur_limit = cast(tuple[int, int], blur_limit)\n        self.angle_range = angle_range\n        self.direction_range = direction_range\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"allow_shifted\",\n            \"angle_range\",\n            \"direction_range\",\n        )\n\n    def apply(self, img: np.ndarray, kernel: np.ndarray, **params: Any) -> np.ndarray:\n        return fmain.convolve(img, kernel=kernel)\n\n    def get_params(self) -> dict[str, Any]:\n        ksize = fblur.sample_odd_from_range(\n            self.py_random,\n            self.blur_limit[0],\n            self.blur_limit[1],\n        )\n\n        angle = self.py_random.uniform(*self.angle_range)\n        direction = self.py_random.uniform(*self.direction_range)\n\n        # Create motion blur kernel\n        kernel = fblur.create_motion_kernel(\n            ksize,\n            angle,\n            direction,\n            allow_shifted=self.allow_shifted,\n            random_state=self.py_random,\n        )\n\n        return {\"kernel\": kernel.astype(np.float32) / np.sum(kernel)}\n
"},{"location":"api_reference/augmentations/blur/transforms/#albumentations.augmentations.blur.transforms.ZoomBlur","title":"class ZoomBlur (max_factor=(1, 1.31), step_factor=(0.01, 0.03), always_apply=None, p=0.5) [view source on GitHub]","text":"

Apply zoom blur transform.

Parameters:

Name Type Description max_factor float, float) or float

range for max factor for blurring. If max_factor is a single float, the range will be (1, limit). Default: (1, 1.31). All max_factor values should be larger than 1.

step_factor float, float) or float

If single float will be used as step parameter for np.arange. If tuple of float step_factor will be in range [step_factor[0], step_factor[1]). Default: (0.01, 0.03). All step_factor values should be positive.

p float

probability of applying the transform. Default: 0.5.

Targets

image

Image types: unit8, float32

Reference

https://arxiv.org/abs/1903.12261

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/blur/transforms.py Python
class ZoomBlur(ImageOnlyTransform):\n    \"\"\"Apply zoom blur transform.\n\n    Args:\n        max_factor ((float, float) or float): range for max factor for blurring.\n            If max_factor is a single float, the range will be (1, limit). Default: (1, 1.31).\n            All max_factor values should be larger than 1.\n        step_factor ((float, float) or float): If single float will be used as step parameter for np.arange.\n            If tuple of float step_factor will be in range `[step_factor[0], step_factor[1])`. Default: (0.01, 0.03).\n            All step_factor values should be positive.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        unit8, float32\n\n    Reference:\n        https://arxiv.org/abs/1903.12261\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        max_factor: OnePlusFloatRangeType\n        step_factor: NonNegativeFloatRangeType\n\n    def __init__(\n        self,\n        max_factor: ScaleFloatType = (1, 1.31),\n        step_factor: ScaleFloatType = (0.01, 0.03),\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_factor = cast(tuple[float, float], max_factor)\n        self.step_factor = cast(tuple[float, float], step_factor)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        zoom_factors: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fblur.zoom_blur(img, zoom_factors)\n\n    def get_params(self) -> dict[str, Any]:\n        step_factor = self.py_random.uniform(*self.step_factor)\n        max_factor = max(1 + step_factor, self.py_random.uniform(*self.max_factor))\n        return {\"zoom_factors\": np.arange(1.0, max_factor, step_factor)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str]:\n        return (\"max_factor\", \"step_factor\")\n
"},{"location":"api_reference/augmentations/crops/","title":"Index","text":"
  • Crop functional transforms (albumentations.augmentations.crops.functional)
  • Crop transforms (albumentations.augmentations.crops.transforms)
"},{"location":"api_reference/augmentations/crops/functional/","title":"Crop functional transforms (augmentations.crops.functional)","text":""},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_and_pad_keypoints","title":"def crop_and_pad_keypoints (keypoints, crop_params=None, pad_params=None, image_shape=(0, 0), result_shape=(0, 0), keep_size=False) [view source on GitHub]","text":"

Crop and pad multiple keypoints simultaneously.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).

crop_params Sequence[int]

Crop parameters [crop_x1, crop_y1, ...].

pad_params Sequence[int]

Pad parameters [top, bottom, left, right].

image_shape Tuple[int, int]

Original image shape (rows, cols).

result_shape Tuple[int, int]

Result image shape (rows, cols).

keep_size bool

Whether to keep the original size.

Returns:

Type Description np.ndarray

Array of transformed keypoints with the same shape as input.

Source code in albumentations/augmentations/crops/functional.py Python
@handle_empty_array(\"keypoints\")\ndef crop_and_pad_keypoints(\n    keypoints: np.ndarray,\n    crop_params: tuple[int, int, int, int] | None = None,\n    pad_params: tuple[int, int, int, int] | None = None,\n    image_shape: tuple[int, int] = (0, 0),\n    result_shape: tuple[int, int] = (0, 0),\n    keep_size: bool = False,\n) -> np.ndarray:\n    \"\"\"Crop and pad multiple keypoints simultaneously.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).\n        crop_params (Sequence[int], optional): Crop parameters [crop_x1, crop_y1, ...].\n        pad_params (Sequence[int], optional): Pad parameters [top, bottom, left, right].\n        image_shape (Tuple[int, int]): Original image shape (rows, cols).\n        result_shape (Tuple[int, int]): Result image shape (rows, cols).\n        keep_size (bool): Whether to keep the original size.\n\n    Returns:\n        np.ndarray: Array of transformed keypoints with the same shape as input.\n    \"\"\"\n    transformed_keypoints = keypoints.copy()\n\n    if crop_params is not None:\n        crop_x1, crop_y1 = crop_params[:2]\n        transformed_keypoints[:, 0] -= crop_x1\n        transformed_keypoints[:, 1] -= crop_y1\n\n    if pad_params is not None:\n        top, _, left, _ = pad_params\n        transformed_keypoints[:, 0] += left\n        transformed_keypoints[:, 1] += top\n\n    rows, cols = image_shape[:2]\n    result_rows, result_cols = result_shape[:2]\n\n    if keep_size and (result_cols != cols or result_rows != rows):\n        scale_x = cols / result_cols\n        scale_y = rows / result_rows\n        return fgeometric.keypoints_scale(transformed_keypoints, scale_x, scale_y)\n\n    return transformed_keypoints\n
"},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_bboxes_by_coords","title":"def crop_bboxes_by_coords (bboxes, crop_coords, image_shape, normalized_input=True) [view source on GitHub]","text":"

Crop bounding boxes based on given crop coordinates.

This function adjusts bounding boxes to fit within a cropped image.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where each row is [x_min, y_min, x_max, y_max, ...]. The bounding box coordinates can be either normalized (in [0, 1]) if normalized_input=True or absolute pixel values if normalized_input=False.

crop_coords tuple[int, int, int, int]

Crop coordinates (x_min, y_min, x_max, y_max) in absolute pixel values.

image_shape tuple[int, int]

Original image shape (height, width).

normalized_input bool

Whether input boxes are in normalized coordinates. If True, assumes input is normalized [0,1] and returns normalized coordinates. If False, assumes input is in absolute pixels and returns absolute coordinates. Default: True for backward compatibility.

Returns:

Type Description np.ndarray

Array of cropped bounding boxes. Coordinates will be in the same format as input (normalized if normalized_input=True, absolute pixels if normalized_input=False).

Note

Bounding boxes that fall completely outside the crop area will be removed. Bounding boxes that partially overlap with the crop area will be adjusted to fit within it.

Source code in albumentations/augmentations/crops/functional.py Python
def crop_bboxes_by_coords(\n    bboxes: np.ndarray,\n    crop_coords: tuple[int, int, int, int],\n    image_shape: tuple[int, int],\n    normalized_input: bool = True,\n) -> np.ndarray:\n    \"\"\"Crop bounding boxes based on given crop coordinates.\n\n    This function adjusts bounding boxes to fit within a cropped image.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (N, 4+) where each row is\n                             [x_min, y_min, x_max, y_max, ...]. The bounding box coordinates\n                             can be either normalized (in [0, 1]) if normalized_input=True or\n                             absolute pixel values if normalized_input=False.\n        crop_coords (tuple[int, int, int, int]): Crop coordinates (x_min, y_min, x_max, y_max)\n                                                 in absolute pixel values.\n        image_shape (tuple[int, int]): Original image shape (height, width).\n        normalized_input (bool): Whether input boxes are in normalized coordinates.\n                               If True, assumes input is normalized [0,1] and returns normalized coordinates.\n                               If False, assumes input is in absolute pixels and returns absolute coordinates.\n                               Default: True for backward compatibility.\n\n    Returns:\n        np.ndarray: Array of cropped bounding boxes. Coordinates will be in the same format as input\n                   (normalized if normalized_input=True, absolute pixels if normalized_input=False).\n\n    Note:\n        Bounding boxes that fall completely outside the crop area will be removed.\n        Bounding boxes that partially overlap with the crop area will be adjusted to fit within it.\n    \"\"\"\n    if not bboxes.size:\n        return bboxes\n\n    # Convert to absolute coordinates if needed\n    if normalized_input:\n        cropped_bboxes = denormalize_bboxes(bboxes.copy().astype(np.float32), image_shape)\n    else:\n        cropped_bboxes = bboxes.copy().astype(np.float32)\n\n    x_min, y_min = crop_coords[:2]\n\n    # Subtract crop coordinates\n    cropped_bboxes[:, [0, 2]] -= x_min\n    cropped_bboxes[:, [1, 3]] -= y_min\n\n    # Calculate crop shape\n    crop_height = crop_coords[3] - crop_coords[1]\n    crop_width = crop_coords[2] - crop_coords[0]\n    crop_shape = (crop_height, crop_width)\n\n    # Return in same format as input\n    return normalize_bboxes(cropped_bboxes, crop_shape) if normalized_input else cropped_bboxes\n
"},{"location":"api_reference/augmentations/crops/functional/#albumentations.augmentations.crops.functional.crop_keypoints_by_coords","title":"def crop_keypoints_by_coords (keypoints, crop_coords) [view source on GitHub]","text":"

Crop keypoints using the provided coordinates of bottom-left and top-right corners in pixels.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).

crop_coords tuple

Crop box coords (x1, y1, x2, y2).

Returns:

Type Description np.ndarray

An array of cropped keypoints with the same shape as the input.

Source code in albumentations/augmentations/crops/functional.py Python
@handle_empty_array(\"keypoints\")\ndef crop_keypoints_by_coords(\n    keypoints: np.ndarray,\n    crop_coords: tuple[int, int, int, int],\n) -> np.ndarray:\n    \"\"\"Crop keypoints using the provided coordinates of bottom-left and top-right corners in pixels.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) where each row is (x, y, angle, scale, ...).\n        crop_coords (tuple): Crop box coords (x1, y1, x2, y2).\n\n    Returns:\n        np.ndarray: An array of cropped keypoints with the same shape as the input.\n    \"\"\"\n    x1, y1 = crop_coords[:2]\n\n    cropped_keypoints = keypoints.copy()\n    cropped_keypoints[:, 0] -= x1  # Adjust x coordinates\n    cropped_keypoints[:, 1] -= y1  # Adjust y coordinates\n\n    return cropped_keypoints\n
"},{"location":"api_reference/augmentations/crops/transforms/","title":"Crop transforms (augmentations.crops.transforms)","text":""},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.AtLeastOneBBoxRandomCrop","title":"class AtLeastOneBBoxRandomCrop (height, width, erosion_factor=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crops an image to a fixed resolution, while ensuring that at least one bounding box is always in the crop. The maximal erosion factor define by how much the target bounding box can be thinned out. For example, erosion_factor = 0.2 means that the bounding box dimensions can be thinned by up to 20%.

Parameters:

Name Type Description height int

Height of the crop.

width int

Width of the crop.

erosion_factor float

Maximal erosion factor of the height and width of the target bounding box. Default: 0.0.

p float

The probability of applying the transform. Default: 1.0.

always_apply bool | None

Whether to apply the transform systematically.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class AtLeastOneBBoxRandomCrop(BaseCrop):\n    \"\"\"Crops an image to a fixed resolution, while ensuring that at least one bounding box is always in the crop.\n    The maximal erosion factor define by how much the target bounding box can be thinned out.\n    For example, erosion_factor = 0.2 means that the bounding box dimensions can be thinned by up to 20%.\n\n    Args:\n        height: Height of the crop.\n        width: Width of the crop.\n        erosion_factor: Maximal erosion factor of the height and width of the target bounding box. Default: 0.0.\n        p: The probability of applying the transform. Default: 1.0.\n        always_apply: Whether to apply the transform systematically.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseCrop.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        erosion_factor: Annotated[float, Field(ge=0.0, le=1.0)]\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        erosion_factor: float = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.erosion_factor = erosion_factor\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_height, image_width = params[\"shape\"][:2]\n        bboxes = data.get(\"bboxes\", [])\n\n        if self.height > image_height or self.width > image_width:\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_height, image_width}\",\n            )\n\n        if len(bboxes) > 0:\n            # Pick a bbox amongst all possible as our reference bbox.\n            bboxes = denormalize_bboxes(bboxes, shape=(image_height, image_width))\n            bbox = self.py_random.choice(bboxes)\n\n            x1, y1, x2, y2 = bbox[:4]\n\n            w = x2 - x1\n            h = y2 - y1\n\n            # Compute the eroded width and height\n            ew = w * (1.0 - self.erosion_factor)\n            eh = h * (1.0 - self.erosion_factor)\n\n            # Compute the lower and upper bounds for the x-axis and y-axis.\n            ax1 = np.clip(\n                a=x1 + ew - self.width,\n                a_min=0.0,\n                a_max=image_width - self.width,\n            )\n            bx1 = np.clip(\n                a=x2 - ew,\n                a_min=0.0,\n                a_max=image_width - self.width,\n            )\n\n            ay1 = np.clip(\n                a=y1 + eh - self.height,\n                a_min=0.0,\n                a_max=image_height - self.height,\n            )\n            by1 = np.clip(\n                a=y2 - eh,\n                a_min=0.0,\n                a_max=image_height - self.height,\n            )\n        else:\n            # If there are no bboxes, just crop anywhere in the image.\n            ax1 = 0.0\n            bx1 = image_width - self.width\n\n            ay1 = 0.0\n            by1 = image_height - self.height\n\n        # Randomly draw the upper-left corner.\n        x1 = int(self.py_random.uniform(a=ax1, b=bx1))\n        y1 = int(self.py_random.uniform(a=ay1, b=by1))\n\n        x2 = x1 + self.width\n        y2 = y1 + self.height\n\n        return {\"crop_coords\": (x1, y1, x2, y2)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"erosion_factor\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BBoxSafeRandomCrop","title":"class BBoxSafeRandomCrop (erosion_rate=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input without loss of bounding boxes.

This transform performs a random crop of the input image while ensuring that all bounding boxes remain within the cropped area. It's particularly useful for object detection tasks where preserving all objects in the image is crucial.

Parameters:

Name Type Description erosion_rate float

A value between 0.0 and 1.0 that determines the minimum allowable size of the crop as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be at least 80% of the original image height. Default: 0.0 (no minimum size).

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

This transform ensures that all bounding boxes in the original image are fully contained within the cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out), it will default to cropping the entire image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.ones((300, 300, 3), dtype=np.uint8)\n>>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n>>> transform = A.Compose([\n...     A.BBoxSafeRandomCrop(erosion_rate=0.2, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n>>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n>>> transformed_image = transformed['image']\n>>> transformed_bboxes = transformed['bboxes']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BBoxSafeRandomCrop(BaseCrop):\n    \"\"\"Crop a random part of the input without loss of bounding boxes.\n\n    This transform performs a random crop of the input image while ensuring that all bounding boxes remain within\n    the cropped area. It's particularly useful for object detection tasks where preserving all objects in the image\n    is crucial.\n\n    Args:\n        erosion_rate (float): A value between 0.0 and 1.0 that determines the minimum allowable size of the crop\n            as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be\n            at least 80% of the original image height. Default: 0.0 (no minimum size).\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transform ensures that all bounding boxes in the original image are fully contained within the\n        cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out),\n        it will default to cropping the entire image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.ones((300, 300, 3), dtype=np.uint8)\n        >>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n        >>> transform = A.Compose([\n        ...     A.BBoxSafeRandomCrop(erosion_rate=0.2, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n        >>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n        >>> transformed_image = transformed['image']\n        >>> transformed_bboxes = transformed['bboxes']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        erosion_rate: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n\n    def __init__(self, erosion_rate: float = 0.0, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p)\n        self.erosion_rate = erosion_rate\n\n    def _get_coords_no_bbox(self, image_shape: tuple[int, int]) -> tuple[int, int, int, int]:\n        image_height, image_width = image_shape\n\n        erosive_h = int(image_height * (1.0 - self.erosion_rate))\n        crop_height = image_height if erosive_h >= image_height else self.py_random.randint(erosive_h, image_height)\n\n        crop_width = int(crop_height * image_width / image_height)\n\n        h_start = self.py_random.random()\n        w_start = self.py_random.random()\n\n        crop_shape = (crop_height, crop_width)\n\n        return fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n\n        if len(data[\"bboxes\"]) == 0:  # less likely, this class is for use with bboxes.\n            crop_coords = self._get_coords_no_bbox(image_shape)\n            return {\"crop_coords\": crop_coords}\n\n        bbox_union = union_of_bboxes(bboxes=data[\"bboxes\"], erosion_rate=self.erosion_rate)\n\n        if bbox_union is None:\n            crop_coords = self._get_coords_no_bbox(image_shape)\n            return {\"crop_coords\": crop_coords}\n\n        x_min, y_min, x_max, y_max = bbox_union\n\n        x_min = np.clip(x_min, 0, 1)\n        y_min = np.clip(y_min, 0, 1)\n        x_max = np.clip(x_max, x_min, 1)\n        y_max = np.clip(y_max, y_min, 1)\n\n        image_height, image_width = image_shape\n\n        crop_x_min = int(x_min * self.py_random.random() * image_width)\n        crop_y_min = int(y_min * self.py_random.random() * image_height)\n\n        bbox_xmax = x_max + (1 - x_max) * self.py_random.random()\n        bbox_ymax = y_max + (1 - y_max) * self.py_random.random()\n        crop_x_max = int(bbox_xmax * image_width)\n        crop_y_max = int(bbox_ymax * image_height)\n\n        return {\"crop_coords\": (crop_x_min, crop_y_min, crop_x_max, crop_y_max)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"erosion_rate\",)\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseCrop","title":"class BaseCrop [view source on GitHub]","text":"

Base class for transforms that only perform cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseCrop(DualTransform):\n    \"\"\"Base class for transforms that only perform cropping.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop(img, x_min=crop_coords[0], y_min=crop_coords[1], x_max=crop_coords[2], y_max=crop_coords[3])\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_bboxes_by_coords(bboxes, crop_coords, params[\"shape\"][:2])\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_keypoints_by_coords(keypoints, crop_coords)\n\n    @staticmethod\n    def _clip_bbox(bbox: tuple[int, int, int, int], image_shape: tuple[int, int]) -> tuple[int, int, int, int]:\n        height, width = image_shape[:2]\n        x_min, y_min, x_max, y_max = bbox\n        x_min = np.clip(x_min, 0, width)\n        y_min = np.clip(y_min, 0, height)\n\n        x_max = np.clip(x_max, x_min, width)\n        y_max = np.clip(y_max, y_min, height)\n        return x_min, y_min, x_max, y_max\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseCropAndPad","title":"class BaseCropAndPad (pad_if_needed, border_mode, fill, fill_mask, pad_position, p, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that need both cropping and padding.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseCropAndPad(BaseCrop):\n    \"\"\"Base class for transforms that need both cropping and padding.\"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        pad_if_needed: bool\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_position: PositionType\n\n    def __init__(\n        self,\n        pad_if_needed: bool,\n        border_mode: int,\n        fill: ColorType,\n        fill_mask: ColorType,\n        pad_position: PositionType,\n        p: float,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p)\n        self.pad_if_needed = pad_if_needed\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.pad_position = pad_position\n\n    def _get_pad_params(self, image_shape: tuple[int, int], target_shape: tuple[int, int]) -> dict[str, Any] | None:\n        \"\"\"Calculate padding parameters if needed.\"\"\"\n        if not self.pad_if_needed:\n            return None\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=image_shape,\n            min_height=target_shape[0],\n            min_width=target_shape[1],\n            pad_height_divisor=None,\n            pad_width_divisor=None,\n        )\n\n        if h_pad_top == h_pad_bottom == w_pad_left == w_pad_right == 0:\n            return None\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.pad_position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        if pad_params is not None:\n            img = fgeometric.pad_with_params(\n                img,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                border_mode=self.border_mode,\n                value=self.fill,\n            )\n        return BaseCrop.apply(self, img, crop_coords, **params)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_coords: Any,\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        if pad_params is not None:\n            mask = fgeometric.pad_with_params(\n                mask,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                border_mode=self.border_mode,\n                value=self.fill_mask,\n            )\n        # Note' that super().apply would apply the padding twice as it is looped to this.apply\n        return BaseCrop.apply(self, mask, crop_coords=crop_coords, **params)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        image_shape = params[\"shape\"][:2]\n\n        if pad_params is not None:\n            # First denormalize bboxes to absolute coordinates\n            bboxes_np = denormalize_bboxes(bboxes, image_shape)\n\n            # Apply padding to bboxes (already works with absolute coordinates)\n            bboxes_np = fgeometric.pad_bboxes(\n                bboxes_np,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                self.border_mode,\n                image_shape=image_shape,\n            )\n\n            # Update shape to padded dimensions\n            padded_height = image_shape[0] + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            padded_width = image_shape[1] + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n            padded_shape = (padded_height, padded_width)\n\n            bboxes_np = normalize_bboxes(bboxes_np, padded_shape)\n\n            params[\"shape\"] = padded_shape\n\n            return BaseCrop.apply_to_bboxes(self, bboxes_np, crop_coords, **params)\n\n        # If no padding, use original function behavior\n        return BaseCrop.apply_to_bboxes(self, bboxes, crop_coords, **params)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        pad_params = params.get(\"pad_params\")\n        image_shape = params[\"shape\"][:2]\n\n        if pad_params is not None:\n            # Calculate padded dimensions\n            padded_height = image_shape[0] + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            padded_width = image_shape[1] + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n            # First apply padding to keypoints using original image shape\n            keypoints = fgeometric.pad_keypoints(\n                keypoints,\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n                self.border_mode,\n                image_shape=image_shape,\n            )\n\n            # Update image shape for subsequent crop operation\n            params = {**params, \"shape\": (padded_height, padded_width)}\n\n        return BaseCrop.apply_to_keypoints(self, keypoints, crop_coords, **params)\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.BaseRandomSizedCropInitSchema","title":"class BaseRandomSizedCropInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class BaseRandomSizedCropInitSchema(BaseTransformInitSchema):\n    size: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CenterCrop","title":"class CenterCrop (height, width, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0.0, fill_mask=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop the central part of the input.

This transform crops the center of the input image, mask, bounding boxes, and keypoints to the specified dimensions. It's useful when you want to focus on the central region of the input, discarding peripheral information.

Parameters:

Name Type Description height int

The height of the crop. Must be greater than 0.

width int

The width of the crop. Must be greater than 0.

pad_if_needed bool

Whether to pad if crop size exceeds image size. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value for images if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks if border_mode is cv2.BORDER_CONSTANT. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If pad_if_needed is False and crop size exceeds image dimensions, it will raise a CropSizeError.
  • If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded.
  • For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CenterCrop(BaseCropAndPad):\n    \"\"\"Crop the central part of the input.\n\n    This transform crops the center of the input image, mask, bounding boxes, and keypoints to the specified dimensions.\n    It's useful when you want to focus on the central region of the input, discarding peripheral information.\n\n    Args:\n        height (int): The height of the crop. Must be greater than 0.\n        width (int): The width of the crop. Must be greater than 0.\n        pad_if_needed (bool): Whether to pad if crop size exceeds image size. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value for images if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If pad_if_needed is False and crop size exceeds image dimensions, it will raise a CropSizeError.\n        - If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded.\n        - For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_dimensions(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n            return self\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0.0,\n        fill_mask: ColorType = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.height = height\n        self.width = width\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"height\",\n            \"width\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        if not self.pad_if_needed and (self.height > image_height or self.width > image_width):\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_shape[:2]}\",\n            )\n\n        # Get padding params first if needed\n        pad_params = self._get_pad_params(image_shape, (self.height, self.width))\n\n        # If padding is needed, adjust the image shape for crop calculation\n        if pad_params is not None:\n            pad_top = pad_params[\"pad_top\"]\n            pad_bottom = pad_params[\"pad_bottom\"]\n            pad_left = pad_params[\"pad_left\"]\n            pad_right = pad_params[\"pad_right\"]\n\n            padded_height = image_height + pad_top + pad_bottom\n            padded_width = image_width + pad_left + pad_right\n            padded_shape = (padded_height, padded_width)\n\n            # Get crop coordinates based on padded dimensions\n            crop_coords = fcrops.get_center_crop_coords(padded_shape, (self.height, self.width))\n        else:\n            # Get crop coordinates based on original dimensions\n            crop_coords = fcrops.get_center_crop_coords(image_shape, (self.height, self.width))\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.Crop","title":"class Crop (x_min=0, y_min=0, x_max=1024, y_max=1024, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a specific region from the input image.

This transform crops a rectangular region from the input image, mask, bounding boxes, and keypoints based on specified coordinates. It's useful when you want to extract a specific area of interest from your inputs.

Parameters:

Name Type Description x_min int

Minimum x-coordinate of the crop region (left edge). Must be >= 0. Default: 0.

y_min int

Minimum y-coordinate of the crop region (top edge). Must be >= 0. Default: 0.

x_max int

Maximum x-coordinate of the crop region (right edge). Must be > x_min. Default: 1024.

y_max int

Maximum y-coordinate of the crop region (bottom edge). Must be > y_min. Default: 1024.

pad_if_needed bool

Whether to pad if crop coordinates exceed image dimensions. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The crop coordinates are applied as follows: x_min <= x < x_max and y_min <= y < y_max.
  • If pad_if_needed is False and crop region extends beyond image boundaries, it will be clipped.
  • If pad_if_needed is True, image will be padded to accommodate the full crop region.
  • For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class Crop(BaseCropAndPad):\n    \"\"\"Crop a specific region from the input image.\n\n    This transform crops a rectangular region from the input image, mask, bounding boxes, and keypoints\n    based on specified coordinates. It's useful when you want to extract a specific area of interest\n    from your inputs.\n\n    Args:\n        x_min (int): Minimum x-coordinate of the crop region (left edge). Must be >= 0. Default: 0.\n        y_min (int): Minimum y-coordinate of the crop region (top edge). Must be >= 0. Default: 0.\n        x_max (int): Maximum x-coordinate of the crop region (right edge). Must be > x_min. Default: 1024.\n        y_max (int): Maximum y-coordinate of the crop region (bottom edge). Must be > y_min. Default: 1024.\n        pad_if_needed (bool): Whether to pad if crop coordinates exceed image dimensions. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The crop coordinates are applied as follows: x_min <= x < x_max and y_min <= y < y_max.\n        - If pad_if_needed is False and crop region extends beyond image boundaries, it will be clipped.\n        - If pad_if_needed is True, image will be padded to accommodate the full crop region.\n        - For bounding boxes and keypoints, coordinates are adjusted appropriately for both padding and cropping.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        x_min: Annotated[int, Field(ge=0)]\n        y_min: Annotated[int, Field(ge=0)]\n        x_max: Annotated[int, Field(gt=0)]\n        y_max: Annotated[int, Field(gt=0)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_coordinates(self) -> Self:\n            if not self.x_min < self.x_max:\n                msg = \"x_max must be greater than x_min\"\n                raise ValueError(msg)\n            if not self.y_min < self.y_max:\n                msg = \"y_max must be greater than y_min\"\n                raise ValueError(msg)\n\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n\n            return self\n\n    def __init__(\n        self,\n        x_min: int = 0,\n        y_min: int = 0,\n        x_max: int = 1024,\n        y_max: int = 1024,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.x_min = x_min\n        self.y_min = y_min\n        self.x_max = x_max\n        self.y_max = y_max\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        crop_height = self.y_max - self.y_min\n        crop_width = self.x_max - self.x_min\n\n        if not self.pad_if_needed:\n            # If no padding, clip coordinates to image boundaries\n            x_min = np.clip(self.x_min, 0, image_width)\n            y_min = np.clip(self.y_min, 0, image_height)\n            x_max = np.clip(self.x_max, x_min, image_width)\n            y_max = np.clip(self.y_max, y_min, image_height)\n            return {\"crop_coords\": (x_min, y_min, x_max, y_max)}\n\n        # Calculate padding if needed\n        pad_params = self._get_pad_params(\n            image_shape=image_shape,\n            target_shape=(max(crop_height, image_height), max(crop_width, image_width)),\n        )\n\n        if pad_params is not None:\n            # Adjust crop coordinates based on padding\n            x_min = self.x_min + pad_params[\"pad_left\"]\n            y_min = self.y_min + pad_params[\"pad_top\"]\n            x_max = self.x_max + pad_params[\"pad_left\"]\n            y_max = self.y_max + pad_params[\"pad_top\"]\n            crop_coords = (x_min, y_min, x_max, y_max)\n        else:\n            crop_coords = (self.x_min, self.y_min, self.x_max, self.y_max)\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"x_min\",\n            \"y_min\",\n            \"x_max\",\n            \"y_max\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CropAndPad","title":"class CropAndPad (px=None, percent=None, pad_mode=None, pad_cval=None, pad_cval_mask=None, keep_size=True, sample_independently=True, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop and pad images by pixel amounts or fractions of image sizes.

This transform allows for simultaneous cropping and padding of images. Cropping removes pixels from the sides (i.e., extracts a subimage), while padding adds pixels to the sides (e.g., black pixels). The amount of cropping/padding can be specified either in absolute pixels or as a fraction of the image size.

Parameters:

Name Type Description px int, tuple of int, tuple of tuples of int, or None

The number of pixels to crop (negative values) or pad (positive values) on each side of the image. Either this or the parameter percent may be set, not both at the same time. - If int: crop/pad all sides by this value. - If tuple of 2 ints: crop/pad by (top/bottom, left/right). - If tuple of 4 ints: crop/pad by (top, right, bottom, left). - Each int can also be a tuple of 2 ints for a range, or a list of ints for discrete choices. Default: None.

percent float, tuple of float, tuple of tuples of float, or None

The fraction of the image size to crop (negative values) or pad (positive values) on each side. Either this or the parameter px may be set, not both at the same time. - If float: crop/pad all sides by this fraction. - If tuple of 2 floats: crop/pad by (top/bottom, left/right) fractions. - If tuple of 4 floats: crop/pad by (top, right, bottom, left) fractions. - Each float can also be a tuple of 2 floats for a range, or a list of floats for discrete choices. Default: None.

border_mode int

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

The constant value to use for padding if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Same as fill but used for mask padding. Default: 0.

keep_size bool

If True, the output image will be resized to the input image size after cropping/padding. Default: True.

sample_independently bool

If True and ranges are used for px/percent, sample a value for each side independently. If False, sample one value and use it for all sides. Default: True.

interpolation int

OpenCV interpolation flag used for resizing if keep_size is True. Default: cv2.INTER_LINEAR.

mask_interpolation int

OpenCV interpolation flag used for resizing if keep_size is True. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform will never crop images below a height or width of 1.
  • When using pixel values (px), the image will be cropped/padded by exactly that many pixels.
  • When using percentages (percent), the amount of crop/pad will be calculated based on the image size.
  • Bounding boxes that end up fully outside the image after cropping will be removed.
  • Keypoints that end up outside the image after cropping will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.CropAndPad(px=(-10, 20, 30, -40), border_mode=cv2.BORDER_REFLECT, fill=128, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CropAndPad(DualTransform):\n    \"\"\"Crop and pad images by pixel amounts or fractions of image sizes.\n\n    This transform allows for simultaneous cropping and padding of images. Cropping removes pixels from the sides\n    (i.e., extracts a subimage), while padding adds pixels to the sides (e.g., black pixels). The amount of\n    cropping/padding can be specified either in absolute pixels or as a fraction of the image size.\n\n    Args:\n        px (int, tuple of int, tuple of tuples of int, or None):\n            The number of pixels to crop (negative values) or pad (positive values) on each side of the image.\n            Either this or the parameter `percent` may be set, not both at the same time.\n            - If int: crop/pad all sides by this value.\n            - If tuple of 2 ints: crop/pad by (top/bottom, left/right).\n            - If tuple of 4 ints: crop/pad by (top, right, bottom, left).\n            - Each int can also be a tuple of 2 ints for a range, or a list of ints for discrete choices.\n            Default: None.\n\n        percent (float, tuple of float, tuple of tuples of float, or None):\n            The fraction of the image size to crop (negative values) or pad (positive values) on each side.\n            Either this or the parameter `px` may be set, not both at the same time.\n            - If float: crop/pad all sides by this fraction.\n            - If tuple of 2 floats: crop/pad by (top/bottom, left/right) fractions.\n            - If tuple of 4 floats: crop/pad by (top, right, bottom, left) fractions.\n            - Each float can also be a tuple of 2 floats for a range, or a list of floats for discrete choices.\n            Default: None.\n\n        border_mode (int):\n            OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n\n        fill (ColorType):\n            The constant value to use for padding if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n\n        fill_mask (ColorType):\n            Same as fill but used for mask padding. Default: 0.\n\n        keep_size (bool):\n            If True, the output image will be resized to the input image size after cropping/padding.\n            Default: True.\n\n        sample_independently (bool):\n            If True and ranges are used for px/percent, sample a value for each side independently.\n            If False, sample one value and use it for all sides. Default: True.\n\n        interpolation (int):\n            OpenCV interpolation flag used for resizing if keep_size is True.\n            Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (int):\n            OpenCV interpolation flag used for resizing if keep_size is True.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float):\n            Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform will never crop images below a height or width of 1.\n        - When using pixel values (px), the image will be cropped/padded by exactly that many pixels.\n        - When using percentages (percent), the amount of crop/pad will be calculated based on the image size.\n        - Bounding boxes that end up fully outside the image after cropping will be removed.\n        - Keypoints that end up outside the image after cropping will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.CropAndPad(px=(-10, 20, 30, -40), border_mode=cv2.BORDER_REFLECT, fill=128, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        px: PxType | None\n        percent: PercentType | None\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n        keep_size: bool\n        sample_independently: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def check_px_percent(self) -> Self:\n            if self.px is None and self.percent is None:\n                msg = \"Both px and percent parameters cannot be None simultaneously.\"\n                raise ValueError(msg)\n            if self.px is not None and self.percent is not None:\n                msg = \"Only px or percent may be set!\"\n                raise ValueError(msg)\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n\n            return self\n\n    def __init__(\n        self,\n        px: int | list[int] | None = None,\n        percent: float | list[float] | None = None,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        keep_size: bool = True,\n        sample_independently: bool = True,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.px = px\n        self.percent = percent\n\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n        self.keep_size = keep_size\n        self.sample_independently = sample_independently\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_params: Sequence[int],\n        pad_params: Sequence[int],\n        fill: ColorType,\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad(\n            img,\n            crop_params,\n            pad_params,\n            fill,\n            params[\"shape\"][:2],\n            self.interpolation,\n            self.border_mode,\n            self.keep_size,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_params: Sequence[int],\n        pad_params: Sequence[int],\n        fill_mask: ColorType,\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad(\n            mask,\n            crop_params,\n            pad_params,\n            fill_mask,\n            params[\"shape\"][:2],\n            self.mask_interpolation,\n            self.border_mode,\n            self.keep_size,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        crop_params: tuple[int, int, int, int],\n        pad_params: tuple[int, int, int, int],\n        result_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad_bboxes(bboxes, crop_params, pad_params, params[\"shape\"][:2], result_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_params: tuple[int, int, int, int],\n        pad_params: tuple[int, int, int, int],\n        result_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fcrops.crop_and_pad_keypoints(\n            keypoints,\n            crop_params,\n            pad_params,\n            params[\"shape\"][:2],\n            result_shape,\n            self.keep_size,\n        )\n\n    @staticmethod\n    def __prevent_zero(val1: int, val2: int, max_val: int) -> tuple[int, int]:\n        regain = abs(max_val) + 1\n        regain1 = regain // 2\n        regain2 = regain // 2\n        if regain1 + regain2 < regain:\n            regain1 += 1\n\n        if regain1 > val1:\n            diff = regain1 - val1\n            regain1 = val1\n            regain2 += diff\n        elif regain2 > val2:\n            diff = regain2 - val2\n            regain2 = val2\n            regain1 += diff\n\n        return val1 - regain1, val2 - regain2\n\n    @staticmethod\n    def _prevent_zero(crop_params: list[int], height: int, width: int) -> list[int]:\n        top, right, bottom, left = crop_params\n\n        remaining_height = height - (top + bottom)\n        remaining_width = width - (left + right)\n\n        if remaining_height < 1:\n            top, bottom = CropAndPad.__prevent_zero(top, bottom, height)\n        if remaining_width < 1:\n            left, right = CropAndPad.__prevent_zero(left, right, width)\n\n        return [max(top, 0), max(right, 0), max(bottom, 0), max(left, 0)]\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        if self.px is not None:\n            new_params = self._get_px_params()\n        else:\n            percent_params = self._get_percent_params()\n            new_params = [\n                int(percent_params[0] * height),\n                int(percent_params[1] * width),\n                int(percent_params[2] * height),\n                int(percent_params[3] * width),\n            ]\n\n        pad_params = [max(i, 0) for i in new_params]\n\n        crop_params = self._prevent_zero([-min(i, 0) for i in new_params], height, width)\n\n        top, right, bottom, left = crop_params\n        crop_params = [left, top, width - right, height - bottom]\n        result_rows = crop_params[3] - crop_params[1]\n        result_cols = crop_params[2] - crop_params[0]\n        if result_cols == width and result_rows == height:\n            crop_params = []\n\n        top, right, bottom, left = pad_params\n        pad_params = [top, bottom, left, right]\n        if any(pad_params):\n            result_rows += top + bottom\n            result_cols += left + right\n        else:\n            pad_params = []\n\n        return {\n            \"crop_params\": crop_params or None,\n            \"pad_params\": pad_params or None,\n            \"fill\": None if pad_params is None else self._get_pad_value(cast(ColorType, self.fill)),\n            \"fill_mask\": None if pad_params is None else self._get_pad_value(cast(ColorType, self.fill_mask)),\n            \"result_shape\": (result_rows, result_cols),\n        }\n\n    def _get_px_params(self) -> list[int]:\n        if self.px is None:\n            msg = \"px is not set\"\n            raise ValueError(msg)\n\n        if isinstance(self.px, int):\n            params = [self.px] * 4\n        elif len(self.px) == PAIR:\n            if self.sample_independently:\n                params = [self.py_random.randrange(*self.px) for _ in range(4)]\n            else:\n                px = self.py_random.randrange(*self.px)\n                params = [px] * 4\n        elif isinstance(self.px[0], int):\n            params = self.px\n        elif len(self.px[0]) == PAIR:\n            params = [self.py_random.randrange(*i) for i in self.px]\n        else:\n            params = [self.py_random.choice(i) for i in self.px]\n\n        return params\n\n    def _get_percent_params(self) -> list[float]:\n        if self.percent is None:\n            msg = \"percent is not set\"\n            raise ValueError(msg)\n\n        if isinstance(self.percent, float):\n            params = [self.percent] * 4\n        elif len(self.percent) == PAIR:\n            if self.sample_independently:\n                params = [self.py_random.uniform(*self.percent) for _ in range(4)]\n            else:\n                px = self.py_random.uniform(*self.percent)\n                params = [px] * 4\n        elif isinstance(self.percent[0], (int, float)):\n            params = self.percent\n        elif len(self.percent[0]) == PAIR:\n            params = [self.py_random.uniform(*i) for i in self.percent]\n        else:\n            params = [self.py_random.choice(i) for i in self.percent]\n\n        return params  # params = [top, right, bottom, left]\n\n    def _get_pad_value(\n        self,\n        fill: ColorType,\n    ) -> int | float:\n        if isinstance(fill, (list, tuple)):\n            if len(fill) == PAIR:\n                a, b = fill\n                if isinstance(a, int) and isinstance(b, int):\n                    return self.py_random.randint(a, b)\n                return self.py_random.uniform(a, b)\n            return self.py_random.choice(fill)\n\n        if isinstance(fill, Real):\n            return fill\n\n        msg = \"fill should be a number or list, or tuple of two numbers.\"\n        raise ValueError(msg)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"px\",\n            \"percent\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"keep_size\",\n            \"sample_independently\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.CropNonEmptyMaskIfExists","title":"class CropNonEmptyMaskIfExists (height, width, ignore_values=None, ignore_channels=None, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop area with mask if mask is non-empty, else make random crop.

This transform attempts to crop a region containing a mask (non-zero pixels). If the mask is empty or not provided, it falls back to a random crop. This is particularly useful for segmentation tasks where you want to focus on regions of interest defined by the mask.

Parameters:

Name Type Description height int

Vertical size of crop in pixels. Must be > 0.

width int

Horizontal size of crop in pixels. Must be > 0.

ignore_values list of int

Values to ignore in mask, 0 values are always ignored. For example, if background value is 5, set ignore_values=[5] to ignore it. Default: None.

ignore_channels list of int

Channels to ignore in mask. For example, if background is the first channel, set ignore_channels=[0] to ignore it. Default: None.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If a mask is provided, the transform will try to crop an area containing non-zero (or non-ignored) pixels.
  • If no suitable area is found in the mask or no mask is provided, it will perform a random crop.
  • The crop size (height, width) must not exceed the original image dimensions.
  • Bounding boxes and keypoints are also cropped along with the image and mask.

Exceptions:

Type Description ValueError

If the specified crop size is larger than the input image dimensions.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.zeros((100, 100), dtype=np.uint8)\n>>> mask[25:75, 25:75] = 1  # Create a non-empty region in the mask\n>>> transform = A.Compose([\n...     A.CropNonEmptyMaskIfExists(height=50, width=50, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n# The resulting crop will likely include part of the non-zero region in the mask\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class CropNonEmptyMaskIfExists(BaseCrop):\n    \"\"\"Crop area with mask if mask is non-empty, else make random crop.\n\n    This transform attempts to crop a region containing a mask (non-zero pixels). If the mask is empty or not provided,\n    it falls back to a random crop. This is particularly useful for segmentation tasks where you want to focus on\n    regions of interest defined by the mask.\n\n    Args:\n        height (int): Vertical size of crop in pixels. Must be > 0.\n        width (int): Horizontal size of crop in pixels. Must be > 0.\n        ignore_values (list of int, optional): Values to ignore in mask, `0` values are always ignored.\n            For example, if background value is 5, set `ignore_values=[5]` to ignore it. Default: None.\n        ignore_channels (list of int, optional): Channels to ignore in mask.\n            For example, if background is the first channel, set `ignore_channels=[0]` to ignore it. Default: None.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If a mask is provided, the transform will try to crop an area containing non-zero (or non-ignored) pixels.\n        - If no suitable area is found in the mask or no mask is provided, it will perform a random crop.\n        - The crop size (height, width) must not exceed the original image dimensions.\n        - Bounding boxes and keypoints are also cropped along with the image and mask.\n\n    Raises:\n        ValueError: If the specified crop size is larger than the input image dimensions.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.zeros((100, 100), dtype=np.uint8)\n        >>> mask[25:75, 25:75] = 1  # Create a non-empty region in the mask\n        >>> transform = A.Compose([\n        ...     A.CropNonEmptyMaskIfExists(height=50, width=50, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        # The resulting crop will likely include part of the non-zero region in the mask\n    \"\"\"\n\n    class InitSchema(BaseCrop.InitSchema):\n        ignore_values: list[int] | None\n        ignore_channels: list[int] | None\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        ignore_values: list[int] | None = None,\n        ignore_channels: list[int] | None = None,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p)\n\n        self.height = height\n        self.width = width\n        self.ignore_values = ignore_values\n        self.ignore_channels = ignore_channels\n\n    def _preprocess_mask(self, mask: np.ndarray) -> np.ndarray:\n        mask_height, mask_width = mask.shape[:2]\n\n        if self.ignore_values is not None:\n            ignore_values_np = np.array(self.ignore_values)\n            mask = np.where(np.isin(mask, ignore_values_np), 0, mask)\n\n        if mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS and self.ignore_channels is not None:\n            target_channels = np.array([ch for ch in range(mask.shape[-1]) if ch not in self.ignore_channels])\n            mask = np.take(mask, target_channels, axis=-1)\n\n        if self.height > mask_height or self.width > mask_width:\n            raise ValueError(\n                f\"Crop size ({self.height},{self.width}) is larger than image ({mask_height},{mask_width})\",\n            )\n\n        return mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        \"\"\"Get crop coordinates based on mask content.\"\"\"\n        if \"mask\" in data:\n            mask = self._preprocess_mask(data[\"mask\"])\n        elif \"masks\" in data and len(data[\"masks\"]):\n            masks = data[\"masks\"]\n            mask = self._preprocess_mask(np.copy(masks[0]))\n            for m in masks[1:]:\n                mask |= self._preprocess_mask(m)\n        else:\n            msg = \"Can not find mask for CropNonEmptyMaskIfExists\"\n            raise RuntimeError(msg)\n\n        mask_height, mask_width = mask.shape[:2]\n\n        if mask.any():\n            # Find non-zero regions in mask\n            mask_sum = mask.sum(axis=-1) if mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS else mask\n            non_zero_yx = np.argwhere(mask_sum)\n            y, x = self.py_random.choice(non_zero_yx)\n\n            # Calculate crop coordinates centered around chosen point\n            x_min = x - self.py_random.randint(0, self.width - 1)\n            y_min = y - self.py_random.randint(0, self.height - 1)\n            x_min = np.clip(x_min, 0, mask_width - self.width)\n            y_min = np.clip(y_min, 0, mask_height - self.height)\n        else:\n            # Random crop if no non-zero regions\n            x_min = self.py_random.randint(0, mask_width - self.width)\n            y_min = self.py_random.randint(0, mask_height - self.height)\n\n        x_max = x_min + self.width\n        y_max = y_min + self.height\n\n        return {\"crop_coords\": (x_min, y_min, x_max, y_max)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"ignore_values\", \"ignore_channels\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCrop","title":"class RandomCrop (height, width, pad_if_needed=False, pad_mode=None, pad_cval=None, pad_cval_mask=None, pad_position='center', border_mode=0, fill=0.0, fill_mask=0.0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input.

Parameters:

Name Type Description height int

height of the crop.

width int

width of the crop.

pad_if_needed bool

Whether to pad if crop size exceeds image size. Default: False.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value for images if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for masks if border_mode is cv2.BORDER_CONSTANT. Default: 0.

pad_position Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']

Position of padding. Default: 'center'.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded before applying the random crop.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCrop(BaseCropAndPad):\n    \"\"\"Crop a random part of the input.\n\n    Args:\n        height: height of the crop.\n        width: width of the crop.\n        pad_if_needed (bool): Whether to pad if crop size exceeds image size. Default: False.\n        border_mode (OpenCV flag): OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value for images if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fill_mask (ColorType): Padding value for masks if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        pad_position (Literal['center', 'top_left', 'top_right', 'bottom_left', 'bottom_right', 'random']):\n            Position of padding. Default: 'center'.\n        p: probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If pad_if_needed is True and crop size exceeds image dimensions, the image will be padded\n        before applying the random crop.\n    \"\"\"\n\n    class InitSchema(BaseCropAndPad.InitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        border_mode: BorderModeType\n        fill: ColorType\n        fill_mask: ColorType\n        pad_mode: BorderModeType | None\n        pad_cval: ColorType | None\n        pad_cval_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_dimensions(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_cval is not None:\n                warn(\"pad_cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_cval\n            if self.pad_cval_mask is not None:\n                warn(\"pad_cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.pad_cval_mask\n            return self\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        pad_if_needed: bool = False,\n        pad_mode: int | None = None,\n        pad_cval: ColorType | None = None,\n        pad_cval_mask: ColorType | None = None,\n        pad_position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0.0,\n        fill_mask: ColorType = 0.0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=pad_position,\n            p=p,\n        )\n        self.height = height\n        self.width = width\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:  # Changed return type to be more flexible\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        if not self.pad_if_needed and (self.height > image_height or self.width > image_width):\n            raise CropSizeError(\n                f\"Crop size (height, width) exceeds image dimensions (height, width):\"\n                f\" {(self.height, self.width)} vs {image_shape[:2]}\",\n            )\n\n        # Get padding params first if needed\n        pad_params = self._get_pad_params(image_shape, (self.height, self.width))\n\n        # If padding is needed, adjust the image shape for crop calculation\n        if pad_params is not None:\n            pad_top = pad_params[\"pad_top\"]\n            pad_bottom = pad_params[\"pad_bottom\"]\n            pad_left = pad_params[\"pad_left\"]\n            pad_right = pad_params[\"pad_right\"]\n\n            padded_height = image_height + pad_top + pad_bottom\n            padded_width = image_width + pad_left + pad_right\n            padded_shape = (padded_height, padded_width)\n\n            # Get random crop coordinates based on padded dimensions\n            h_start = self.py_random.random()\n            w_start = self.py_random.random()\n            crop_coords = fcrops.get_crop_coords(padded_shape, (self.height, self.width), h_start, w_start)\n        else:\n            # Get random crop coordinates based on original dimensions\n            h_start = self.py_random.random()\n            w_start = self.py_random.random()\n            crop_coords = fcrops.get_crop_coords(image_shape, (self.height, self.width), h_start, w_start)\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"height\",\n            \"width\",\n            \"pad_if_needed\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"pad_position\",\n        )\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCropFromBorders","title":"class RandomCropFromBorders (crop_left=0.1, crop_right=0.1, crop_top=0.1, crop_bottom=0.1, always_apply=None, p=1.0) [view source on GitHub]","text":"

Randomly crops the input from its borders without resizing.

This transform randomly crops parts of the input (image, mask, bounding boxes, or keypoints) from each of its borders. The amount of cropping is specified as a fraction of the input's dimensions for each side independently.

Parameters:

Name Type Description crop_left float

The maximum fraction of width to crop from the left side. Must be in the range [0.0, 1.0]. Default: 0.1

crop_right float

The maximum fraction of width to crop from the right side. Must be in the range [0.0, 1.0]. Default: 0.1

crop_top float

The maximum fraction of height to crop from the top. Must be in the range [0.0, 1.0]. Default: 0.1

crop_bottom float

The maximum fraction of height to crop from the bottom. Must be in the range [0.0, 1.0]. Default: 0.1

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The actual amount of cropping for each side is randomly chosen between 0 and the specified maximum for each application of the transform.
  • The sum of crop_left and crop_right must not exceed 1.0, and the sum of crop_top and crop_bottom must not exceed 1.0. Otherwise, a ValueError will be raised.
  • This transform does not resize the input after cropping, so the output dimensions will be smaller than the input dimensions.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomCropFromBorders(\n...     crop_left=0.1, crop_right=0.2, crop_top=0.2, crop_bottom=0.1, p=1.0\n... )\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image will have random crops from each border, with the maximum\n# possible crops being 10% from the left, 20% from the right, 20% from the top,\n# and 10% from the bottom. The image size will be reduced accordingly.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCropFromBorders(BaseCrop):\n    \"\"\"Randomly crops the input from its borders without resizing.\n\n    This transform randomly crops parts of the input (image, mask, bounding boxes, or keypoints)\n    from each of its borders. The amount of cropping is specified as a fraction of the input's\n    dimensions for each side independently.\n\n    Args:\n        crop_left (float): The maximum fraction of width to crop from the left side.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_right (float): The maximum fraction of width to crop from the right side.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_top (float): The maximum fraction of height to crop from the top.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        crop_bottom (float): The maximum fraction of height to crop from the bottom.\n            Must be in the range [0.0, 1.0]. Default: 0.1\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual amount of cropping for each side is randomly chosen between 0 and\n          the specified maximum for each application of the transform.\n        - The sum of crop_left and crop_right must not exceed 1.0, and the sum of\n          crop_top and crop_bottom must not exceed 1.0. Otherwise, a ValueError will be raised.\n        - This transform does not resize the input after cropping, so the output dimensions\n          will be smaller than the input dimensions.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomCropFromBorders(\n        ...     crop_left=0.1, crop_right=0.2, crop_top=0.2, crop_bottom=0.1, p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image will have random crops from each border, with the maximum\n        # possible crops being 10% from the left, 20% from the right, 20% from the top,\n        # and 10% from the bottom. The image size will be reduced accordingly.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        crop_left: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_right: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_top: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        crop_bottom: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n\n        @model_validator(mode=\"after\")\n        def validate_crop_values(self) -> Self:\n            if self.crop_left + self.crop_right > 1.0:\n                msg = \"The sum of crop_left and crop_right must be <= 1.\"\n                raise ValueError(msg)\n            if self.crop_top + self.crop_bottom > 1.0:\n                msg = \"The sum of crop_top and crop_bottom must be <= 1.\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        crop_left: float = 0.1,\n        crop_right: float = 0.1,\n        crop_top: float = 0.1,\n        crop_bottom: float = 0.1,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p)\n        self.crop_left = crop_left\n        self.crop_right = crop_right\n        self.crop_top = crop_top\n        self.crop_bottom = crop_bottom\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        height, width = params[\"shape\"][:2]\n\n        x_min = self.py_random.randint(0, int(self.crop_left * width))\n        x_max = self.py_random.randint(max(x_min + 1, int((1 - self.crop_right) * width)), width)\n\n        y_min = self.py_random.randint(0, int(self.crop_top * height))\n        y_max = self.py_random.randint(max(y_min + 1, int((1 - self.crop_bottom) * height)), height)\n\n        crop_coords = x_min, y_min, x_max, y_max\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"crop_left\", \"crop_right\", \"crop_top\", \"crop_bottom\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomCropNearBBox","title":"class RandomCropNearBBox (max_part_shift=(0, 0.3), cropping_bbox_key='cropping_bbox', cropping_box_key=None, always_apply=None, p=1.0) [view source on GitHub]","text":"

Crop bbox from image with random shift by x,y coordinates

Parameters:

Name Type Description max_part_shift float, (float, float

Max shift in height and width dimensions relative to cropping_bbox dimension. If max_part_shift is a single float, the range will be (0, max_part_shift). Default (0, 0.3).

cropping_bbox_key str

Additional target key for cropping box. Default cropping_bbox.

cropping_box_key str

[Deprecated] Use cropping_bbox_key instead.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> aug = Compose([RandomCropNearBBox(max_part_shift=(0.1, 0.5), cropping_bbox_key='test_bbox')],\n>>>              bbox_params=BboxParams(\"pascal_voc\"))\n>>> result = aug(image=image, bboxes=bboxes, test_bbox=[0, 5, 10, 20])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomCropNearBBox(BaseCrop):\n    \"\"\"Crop bbox from image with random shift by x,y coordinates\n\n    Args:\n        max_part_shift (float, (float, float)): Max shift in `height` and `width` dimensions relative\n            to `cropping_bbox` dimension.\n            If max_part_shift is a single float, the range will be (0, max_part_shift).\n            Default (0, 0.3).\n        cropping_bbox_key (str): Additional target key for cropping box. Default `cropping_bbox`.\n        cropping_box_key (str): [Deprecated] Use `cropping_bbox_key` instead.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Examples:\n        >>> aug = Compose([RandomCropNearBBox(max_part_shift=(0.1, 0.5), cropping_bbox_key='test_bbox')],\n        >>>              bbox_params=BboxParams(\"pascal_voc\"))\n        >>> result = aug(image=image, bboxes=bboxes, test_bbox=[0, 5, 10, 20])\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_part_shift: ZeroOneRangeType\n        cropping_bbox_key: str\n\n    def __init__(\n        self,\n        max_part_shift: ScaleFloatType = (0, 0.3),\n        cropping_bbox_key: str = \"cropping_bbox\",\n        cropping_box_key: str | None = None,  # Deprecated\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(p=p)\n        # Check for deprecated parameter and issue warning\n        if cropping_box_key is not None:\n            warn(\n                \"The parameter 'cropping_box_key' is deprecated and will be removed in future versions. \"\n                \"Use 'cropping_bbox_key' instead.\",\n                DeprecationWarning,\n                stacklevel=2,\n            )\n            # Ensure the new parameter is used even if the old one is passed\n            cropping_bbox_key = cropping_box_key\n\n        self.max_part_shift = cast(tuple[float, float], max_part_shift)\n        self.cropping_bbox_key = cropping_bbox_key\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[float, ...]]:\n        bbox = data[self.cropping_bbox_key]\n\n        image_shape = params[\"shape\"][:2]\n\n        bbox = self._clip_bbox(bbox, image_shape)\n\n        h_max_shift = round((bbox[3] - bbox[1]) * self.max_part_shift[0])\n        w_max_shift = round((bbox[2] - bbox[0]) * self.max_part_shift[1])\n\n        x_min = bbox[0] - self.py_random.randint(-w_max_shift, w_max_shift)\n        x_max = bbox[2] + self.py_random.randint(-w_max_shift, w_max_shift)\n\n        y_min = bbox[1] - self.py_random.randint(-h_max_shift, h_max_shift)\n        y_max = bbox[3] + self.py_random.randint(-h_max_shift, h_max_shift)\n\n        crop_coords = self._clip_bbox((x_min, y_min, x_max, y_max), image_shape)\n\n        if crop_coords[0] == crop_coords[2] or crop_coords[1] == crop_coords[3]:\n            crop_shape = (bbox[3] - bbox[1], bbox[2] - bbox[0])\n            crop_coords = fcrops.get_center_crop_coords(image_shape, crop_shape)\n\n        return {\"crop_coords\": crop_coords}\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [self.cropping_bbox_key]\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_part_shift\", \"cropping_bbox_key\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomResizedCrop","title":"class RandomResizedCrop (size=None, width=None, height=None, *, scale=(0.08, 1.0), ratio=(0.75, 1.3333333333333333), interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specified size.

This transform first crops a random portion of the input image (or mask, bounding boxes, keypoints) and then resizes the crop to a specified size. It's particularly useful for training neural networks on images of varying sizes and aspect ratios.

Parameters:

Name Type Description size tuple[int, int]

Target size for the output image, i.e. (height, width) after crop and resize.

scale tuple[float, float]

Range of the random size of the crop relative to the input size. For example, (0.08, 1.0) means the crop size will be between 8% and 100% of the input size. Default: (0.08, 1.0)

ratio tuple[float, float]

Range of aspect ratios of the random crop. For example, (0.75, 1.3333) allows crop aspect ratios from 3:4 to 4:3. Default: (0.75, 1.3333333333333333)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform attempts to crop a random area with an aspect ratio and relative size specified by 'ratio' and 'scale' parameters. If it fails to find a suitable crop after 10 attempts, it will return a crop from the center of the image.
  • The crop's aspect ratio is defined as width / height.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.
  • After cropping, the result is resized to the specified size.

Mathematical Details: 1. A target area A is sampled from the range [scale[0] * input_area, scale[1] * input_area]. 2. A target aspect ratio r is sampled from the range [ratio[0], ratio[1]]. 3. The crop width and height are computed as: w = sqrt(A * r) h = sqrt(A / r) 4. If w and h are within the input image dimensions, the crop is accepted. Otherwise, steps 1-3 are repeated (up to 10 times). 5. If no valid crop is found after 10 attempts, a centered crop is taken. 6. The crop is then resized to the specified size.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomResizedCrop(size=80, scale=(0.5, 1.0), ratio=(0.75, 1.33), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# transformed_image will be a 80x80 crop from a random location in the original image,\n# with the crop's size between 50% and 100% of the original image size,\n# and the crop's aspect ratio between 3:4 and 4:3.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomResizedCrop(_BaseRandomSizedCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specified size.\n\n    This transform first crops a random portion of the input image (or mask, bounding boxes, keypoints)\n    and then resizes the crop to a specified size. It's particularly useful for training neural networks\n    on images of varying sizes and aspect ratios.\n\n    Args:\n        size (tuple[int, int]): Target size for the output image, i.e. (height, width) after crop and resize.\n        scale (tuple[float, float]): Range of the random size of the crop relative to the input size.\n            For example, (0.08, 1.0) means the crop size will be between 8% and 100% of the input size.\n            Default: (0.08, 1.0)\n        ratio (tuple[float, float]): Range of aspect ratios of the random crop.\n            For example, (0.75, 1.3333) allows crop aspect ratios from 3:4 to 4:3.\n            Default: (0.75, 1.3333333333333333)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform attempts to crop a random area with an aspect ratio and relative size\n          specified by 'ratio' and 'scale' parameters. If it fails to find a suitable crop after\n          10 attempts, it will return a crop from the center of the image.\n        - The crop's aspect ratio is defined as width / height.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n        - After cropping, the result is resized to the specified size.\n\n    Mathematical Details:\n        1. A target area A is sampled from the range [scale[0] * input_area, scale[1] * input_area].\n        2. A target aspect ratio r is sampled from the range [ratio[0], ratio[1]].\n        3. The crop width and height are computed as:\n           w = sqrt(A * r)\n           h = sqrt(A / r)\n        4. If w and h are within the input image dimensions, the crop is accepted.\n           Otherwise, steps 1-3 are repeated (up to 10 times).\n        5. If no valid crop is found after 10 attempts, a centered crop is taken.\n        6. The crop is then resized to the specified size.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomResizedCrop(size=80, scale=(0.5, 1.0), ratio=(0.75, 1.33), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # transformed_image will be a 80x80 crop from a random location in the original image,\n        # with the crop's size between 50% and 100% of the original image size,\n        # and the crop's aspect ratio between 3:4 and 4:3.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1)), AfterValidator(nondecreasing)]\n        ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        width: int | None\n        height: int | None\n        size: ScaleIntType | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def process(self) -> Self:\n            if isinstance(self.size, int):\n                if isinstance(self.width, int):\n                    warn(\n                        \"Initializing with 'size' as an integer and a separate 'width', `height` are deprecated. \"\n                        \"Please use a tuple (height, width) for the 'size' argument.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                    self.size = (self.size, self.width)\n                else:\n                    msg = \"If size is an integer, width as integer must be specified.\"\n                    raise TypeError(msg)\n\n            if self.size is None:\n                if self.height is None or self.width is None:\n                    message = \"If 'size' is not provided, both 'height' and 'width' must be specified.\"\n                    raise ValueError(message)\n                self.size = (self.height, self.width)\n\n            return self\n\n    def __init__(\n        self,\n        # NOTE @zetyquickly: when (width, height) are deprecated, make 'size' non optional\n        size: ScaleIntType | None = None,\n        width: int | None = None,\n        height: int | None = None,\n        *,\n        scale: tuple[float, float] = (0.08, 1.0),\n        ratio: tuple[float, float] = (0.75, 1.3333333333333333),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            size=cast(tuple[int, int], size),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale = scale\n        self.ratio = ratio\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n        image_height, image_width = image_shape\n\n        area = image_height * image_width\n\n        for _ in range(10):\n            target_area = self.py_random.uniform(*self.scale) * area\n            log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1]))\n            aspect_ratio = math.exp(self.py_random.uniform(*log_ratio))\n\n            width = int(round(math.sqrt(target_area * aspect_ratio)))\n            height = int(round(math.sqrt(target_area / aspect_ratio)))\n\n            if 0 < width <= image_width and 0 < height <= image_height:\n                i = self.py_random.randint(0, image_height - height)\n                j = self.py_random.randint(0, image_width - width)\n\n                h_start = i * 1.0 / (image_height - height + 1e-10)\n                w_start = j * 1.0 / (image_width - width + 1e-10)\n\n                crop_shape = (height, width)\n\n                crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n                return {\"crop_coords\": crop_coords}\n\n        # Fallback to central crop\n        in_ratio = image_width / image_height\n        if in_ratio < min(self.ratio):\n            width = image_width\n            height = int(round(image_width / min(self.ratio)))\n        elif in_ratio > max(self.ratio):\n            height = image_height\n            width = int(round(height * max(self.ratio)))\n        else:  # whole image\n            width = image_width\n            height = image_height\n\n        i = (image_height - height) // 2\n        j = (image_width - width) // 2\n\n        h_start = i * 1.0 / (image_height - height + 1e-10)\n        w_start = j * 1.0 / (image_width - width + 1e-10)\n\n        crop_shape = (height, width)\n\n        crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"scale\", \"ratio\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomSizedBBoxSafeCrop","title":"class RandomSizedBBoxSafeCrop (height, width, erosion_rate=0.0, interpolation=1, mask_interpolation=0, always_apply=None, p=1.0) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specific size without loss of bounding boxes.

This transform first attempts to crop a random portion of the input image while ensuring that all bounding boxes remain within the cropped area. It then resizes the crop to the specified size. This is particularly useful for object detection tasks where preserving all objects in the image is crucial while also standardizing the image size.

Parameters:

Name Type Description height int

Height of the output image after resizing.

width int

Width of the output image after resizing.

erosion_rate float

A value between 0.0 and 1.0 that determines the minimum allowable size of the crop as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be at least 80% of the original image height and width. Default: 0.0 (no minimum size).

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform ensures that all bounding boxes in the original image are fully contained within the cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out), it will default to cropping the entire image.
  • After cropping, the result is resized to the specified (height, width) size.
  • Bounding box coordinates are adjusted to match the new image size.
  • Keypoints are moved along with the crop and scaled to the new image size.
  • If there are no bounding boxes in the image, it will fall back to a random crop.

Mathematical Details: 1. A crop region is selected that includes all bounding boxes. 2. The crop size is determined by the erosion_rate: min_crop_size = (1 - erosion_rate) * original_size 3. If the selected crop is smaller than min_crop_size, it's expanded to meet this requirement. 4. The crop is then resized to the specified (height, width) size. 5. Bounding box coordinates are transformed to match the new image size: new_coord = (old_coord - crop_start) * (new_size / crop_size)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (300, 300, 3), dtype=np.uint8)\n>>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n>>> transform = A.Compose([\n...     A.RandomSizedBBoxSafeCrop(height=224, width=224, erosion_rate=0.2, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n>>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n>>> transformed_image = transformed['image']\n>>> transformed_bboxes = transformed['bboxes']\n# transformed_image will be a 224x224 image containing all original bounding boxes,\n# with their coordinates adjusted to the new image size.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomSizedBBoxSafeCrop(BBoxSafeRandomCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specific size without loss of bounding boxes.\n\n    This transform first attempts to crop a random portion of the input image while ensuring that all bounding boxes\n    remain within the cropped area. It then resizes the crop to the specified size. This is particularly useful for\n    object detection tasks where preserving all objects in the image is crucial while also standardizing the image size.\n\n    Args:\n        height (int): Height of the output image after resizing.\n        width (int): Width of the output image after resizing.\n        erosion_rate (float): A value between 0.0 and 1.0 that determines the minimum allowable size of the crop\n            as a fraction of the original image size. For example, an erosion_rate of 0.2 means the crop will be\n            at least 80% of the original image height and width. Default: 0.0 (no minimum size).\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform ensures that all bounding boxes in the original image are fully contained within the\n          cropped area. If it's not possible to find such a crop (e.g., when bounding boxes are too spread out),\n          it will default to cropping the entire image.\n        - After cropping, the result is resized to the specified (height, width) size.\n        - Bounding box coordinates are adjusted to match the new image size.\n        - Keypoints are moved along with the crop and scaled to the new image size.\n        - If there are no bounding boxes in the image, it will fall back to a random crop.\n\n    Mathematical Details:\n        1. A crop region is selected that includes all bounding boxes.\n        2. The crop size is determined by the erosion_rate:\n           min_crop_size = (1 - erosion_rate) * original_size\n        3. If the selected crop is smaller than min_crop_size, it's expanded to meet this requirement.\n        4. The crop is then resized to the specified (height, width) size.\n        5. Bounding box coordinates are transformed to match the new image size:\n           new_coord = (old_coord - crop_start) * (new_size / crop_size)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (300, 300, 3), dtype=np.uint8)\n        >>> bboxes = [(10, 10, 50, 50), (100, 100, 150, 150)]\n        >>> transform = A.Compose([\n        ...     A.RandomSizedBBoxSafeCrop(height=224, width=224, erosion_rate=0.2, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))\n        >>> transformed = transform(image=image, bboxes=bboxes, labels=['cat', 'dog'])\n        >>> transformed_image = transformed['image']\n        >>> transformed_bboxes = transformed['bboxes']\n        # transformed_image will be a 224x224 image containing all original bounding boxes,\n        # with their coordinates adjusted to the new image size.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: Annotated[int, Field(ge=1)]\n        width: Annotated[int, Field(ge=1)]\n        erosion_rate: float = Field(\n            ge=0.0,\n            le=1.0,\n        )\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        erosion_rate: float = 0.0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        always_apply: bool | None = None,\n        p: float = 1.0,\n    ):\n        super().__init__(erosion_rate=erosion_rate, p=p)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        crop = fcrops.crop(img, *crop_coords)\n        return fgeometric.resize(crop, (self.height, self.width), self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        crop = fcrops.crop(mask, *crop_coords)\n        return fgeometric.resize(crop, (self.height, self.width), self.mask_interpolation)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints = fcrops.crop_keypoints_by_coords(keypoints, crop_coords)\n\n        crop_height = crop_coords[3] - crop_coords[1]\n        crop_width = crop_coords[2] - crop_coords[0]\n\n        scale_y = self.height / crop_height\n        scale_x = self.width / crop_width\n        return fgeometric.keypoints_scale(keypoints, scale_x=scale_x, scale_y=scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"height\", \"width\", \"interpolation\", \"mask_interpolation\")\n
"},{"location":"api_reference/augmentations/crops/transforms/#albumentations.augmentations.crops.transforms.RandomSizedCrop","title":"class RandomSizedCrop (min_max_height, size=None, width=None, height=None, *, w2h_ratio=1.0, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop a random part of the input and rescale it to a specific size.

This transform first crops a random portion of the input and then resizes it to a specified size. The size of the random crop is controlled by the 'min_max_height' parameter.

Parameters:

Name Type Description min_max_height tuple[int, int]

Minimum and maximum height of the crop in pixels.

size tuple[int, int]

Target size for the output image, i.e. (height, width) after crop and resize.

w2h_ratio float

Aspect ratio (width/height) of crop. Default: 1.0

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.0

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The crop size is randomly selected for each execution within the range specified by 'min_max_height'.
  • The aspect ratio of the crop is determined by the 'w2h_ratio' parameter.
  • After cropping, the result is resized to the specified 'size'.
  • Bounding boxes that end up fully outside the cropped area will be removed.
  • Keypoints that end up outside the cropped area will be removed.
  • This transform differs from RandomResizedCrop in that it allows more control over the crop size through the 'min_max_height' parameter, rather than using a scale parameter.

Mathematical Details: 1. A random crop height h is sampled from the range [min_max_height[0], min_max_height[1]]. 2. The crop width w is calculated as: w = h * w2h_ratio 3. A random location for the crop is selected within the input image. 4. The image is cropped to the size (h, w). 5. The crop is then resized to the specified 'size'.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomSizedCrop(\n...     min_max_height=(50, 80),\n...     size=(64, 64),\n...     w2h_ratio=1.0,\n...     interpolation=cv2.INTER_LINEAR,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# transformed_image will be a 64x64 image, resulting from a crop with height\n# between 50 and 80 pixels, and the same aspect ratio as specified by w2h_ratio,\n# taken from a random location in the original image and then resized.\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/crops/transforms.py Python
class RandomSizedCrop(_BaseRandomSizedCrop):\n    \"\"\"Crop a random part of the input and rescale it to a specific size.\n\n    This transform first crops a random portion of the input and then resizes it to a specified size.\n    The size of the random crop is controlled by the 'min_max_height' parameter.\n\n    Args:\n        min_max_height (tuple[int, int]): Minimum and maximum height of the crop in pixels.\n        size (tuple[int, int]): Target size for the output image, i.e. (height, width) after crop and resize.\n        w2h_ratio (float): Aspect ratio (width/height) of crop. Default: 1.0\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The crop size is randomly selected for each execution within the range specified by 'min_max_height'.\n        - The aspect ratio of the crop is determined by the 'w2h_ratio' parameter.\n        - After cropping, the result is resized to the specified 'size'.\n        - Bounding boxes that end up fully outside the cropped area will be removed.\n        - Keypoints that end up outside the cropped area will be removed.\n        - This transform differs from RandomResizedCrop in that it allows more control over the crop size\n          through the 'min_max_height' parameter, rather than using a scale parameter.\n\n    Mathematical Details:\n        1. A random crop height h is sampled from the range [min_max_height[0], min_max_height[1]].\n        2. The crop width w is calculated as: w = h * w2h_ratio\n        3. A random location for the crop is selected within the input image.\n        4. The image is cropped to the size (h, w).\n        5. The crop is then resized to the specified 'size'.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomSizedCrop(\n        ...     min_max_height=(50, 80),\n        ...     size=(64, 64),\n        ...     w2h_ratio=1.0,\n        ...     interpolation=cv2.INTER_LINEAR,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # transformed_image will be a 64x64 image, resulting from a crop with height\n        # between 50 and 80 pixels, and the same aspect ratio as specified by w2h_ratio,\n        # taken from a random location in the original image and then resized.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        min_max_height: OnePlusIntRangeType\n        w2h_ratio: Annotated[float, Field(gt=0)]\n        width: int | None\n        height: int | None\n        size: ScaleIntType | None\n\n        @model_validator(mode=\"after\")\n        def process(self) -> Self:\n            if isinstance(self.size, int):\n                if isinstance(self.width, int):\n                    warn(\n                        \"Initializing with 'size' as an integer and a separate 'width', `height` are deprecated. \"\n                        \"Please use a tuple (height, width) for the 'size' argument.\",\n                        DeprecationWarning,\n                        stacklevel=2,\n                    )\n                    self.size = (self.size, self.width)\n                else:\n                    msg = \"If size is an integer, width as integer must be specified.\"\n                    raise TypeError(msg)\n\n            if self.size is None:\n                if self.height is None or self.width is None:\n                    message = \"If 'size' is not provided, both 'height' and 'width' must be specified.\"\n                    raise ValueError(message)\n                self.size = (self.height, self.width)\n            return self\n\n    def __init__(\n        self,\n        min_max_height: tuple[int, int],\n        # NOTE @zetyquickly: when (width, height) are deprecated, make 'size' non optional\n        size: ScaleIntType | None = None,\n        width: int | None = None,\n        height: int | None = None,\n        *,\n        w2h_ratio: float = 1.0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            size=cast(tuple[int, int], size),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.min_max_height = min_max_height\n        self.w2h_ratio = w2h_ratio\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, tuple[int, int, int, int]]:\n        image_shape = params[\"shape\"][:2]\n\n        crop_height = self.py_random.randint(*self.min_max_height)\n        crop_width = int(crop_height * self.w2h_ratio)\n\n        crop_shape = (crop_height, crop_width)\n\n        h_start = self.py_random.random()\n        w_start = self.py_random.random()\n\n        crop_coords = fcrops.get_crop_coords(image_shape, crop_shape, h_start, w_start)\n\n        return {\"crop_coords\": crop_coords}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"min_max_height\", \"w2h_ratio\")\n
"},{"location":"api_reference/augmentations/domain_adaptation/","title":"Index","text":"
  • Domain Adaptation functional transforms (albumentations.augmentations.domain_adaptation.functional)
  • Domain Adaptation transforms (albumentations.augmentations.domain_adaptation.transforms)
"},{"location":"api_reference/augmentations/domain_adaptation/functional/","title":"Domain Adaptation functional transforms (augmentations.domain_adaptation.functional)","text":""},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.apply_histogram","title":"def apply_histogram (img, reference_image, blend_ratio) [view source on GitHub]","text":"

Apply histogram matching to an input image using a reference image and blend the result.

This function performs histogram matching between the input image and a reference image, then blends the result with the original input image based on the specified blend ratio.

Parameters:

Name Type Description img np.ndarray

The input image to be transformed. Can be either grayscale or RGB. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

reference_image np.ndarray

The reference image used for histogram matching. Should have the same number of channels as the input image. Supported dtypes: uint8, float32 (values should be in [0, 1] range).

blend_ratio float

The ratio for blending the matched image with the original image. Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.

Returns:

Type Description np.ndarray

The transformed image after histogram matching and blending. The output will have the same shape and dtype as the input image.

Supported image types: - Grayscale images: 2D arrays - RGB images: 3D arrays with 3 channels - Multispectral images: 3D arrays with more than 3 channels

Note

  • If the input and reference images have different sizes, the reference image will be resized to match the input image's dimensions.
  • The function uses a custom implementation of histogram matching based on OpenCV and NumPy.
  • The @clipped and @preserve_channel_dim decorators ensure the output is within the valid range and maintains the original number of dimensions.
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef apply_histogram(img: np.ndarray, reference_image: np.ndarray, blend_ratio: float) -> np.ndarray:\n    \"\"\"Apply histogram matching to an input image using a reference image and blend the result.\n\n    This function performs histogram matching between the input image and a reference image,\n    then blends the result with the original input image based on the specified blend ratio.\n\n    Args:\n        img (np.ndarray): The input image to be transformed. Can be either grayscale or RGB.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        reference_image (np.ndarray): The reference image used for histogram matching.\n            Should have the same number of channels as the input image.\n            Supported dtypes: uint8, float32 (values should be in [0, 1] range).\n        blend_ratio (float): The ratio for blending the matched image with the original image.\n            Should be in the range [0, 1], where 0 means no change and 1 means full histogram matching.\n\n    Returns:\n        np.ndarray: The transformed image after histogram matching and blending.\n            The output will have the same shape and dtype as the input image.\n\n    Supported image types:\n        - Grayscale images: 2D arrays\n        - RGB images: 3D arrays with 3 channels\n        - Multispectral images: 3D arrays with more than 3 channels\n\n    Note:\n        - If the input and reference images have different sizes, the reference image\n          will be resized to match the input image's dimensions.\n        - The function uses a custom implementation of histogram matching based on OpenCV and NumPy.\n        - The @clipped and @preserve_channel_dim decorators ensure the output is within\n          the valid range and maintains the original number of dimensions.\n    \"\"\"\n    # Resize reference image only if necessary\n    if img.shape[:2] != reference_image.shape[:2]:\n        reference_image = cv2.resize(reference_image, dsize=(img.shape[1], img.shape[0]))\n\n    img = np.squeeze(img)\n    reference_image = np.squeeze(reference_image)\n\n    # Match histograms between the images\n    matched = match_histograms(img, reference_image)\n\n    # Blend the original image and the matched image\n    return add_weighted(matched, blend_ratio, img, 1 - blend_ratio)\n
"},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.fourier_domain_adaptation","title":"def fourier_domain_adaptation (img, target_img, beta) [view source on GitHub]","text":"

Apply Fourier Domain Adaptation to the input image using a target image.

This function performs domain adaptation in the frequency domain by modifying the amplitude spectrum of the source image based on the target image's amplitude spectrum. It preserves the phase information of the source image, which helps maintain its content while adapting its style to match the target image.

Parameters:

Name Type Description img np.ndarray

The source image to be adapted. Can be grayscale or RGB.

target_img np.ndarray

The target image used as a reference for adaptation. Should have the same dimensions as the source image.

beta float

The adaptation strength, typically in the range [0, 1]. Higher values result in stronger adaptation towards the target image's style.

Returns:

Type Description np.ndarray

The adapted image with the same shape and type as the input image.

Exceptions:

Type Description ValueError

If the source and target images have different shapes.

Note

  • Both input images are converted to float32 for processing.
  • The function handles both grayscale (2D) and color (3D) images.
  • For grayscale images, an extra dimension is added to facilitate uniform processing.
  • The adaptation is performed channel-wise for color images.
  • The output is clipped to the valid range and preserves the original number of channels.

The adaptation process involves the following steps for each channel: 1. Compute the 2D Fourier Transform of both source and target images. 2. Shift the zero frequency component to the center of the spectrum. 3. Extract amplitude and phase information from the source image's spectrum. 4. Mutate the source amplitude using the target amplitude and the beta parameter. 5. Combine the mutated amplitude with the original phase. 6. Perform the inverse Fourier Transform to obtain the adapted channel.

The low_freq_mutate function (not shown here) is responsible for the actual amplitude mutation, focusing on low-frequency components which carry style information.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n>>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n>>> assert adapted_img.shape == source_img.shape\n

References

  • \"FDA: Fourier Domain Adaptation for Semantic Segmentation\" (Yang and Soatto, 2020, CVPR) https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf
Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@clipped\n@preserve_channel_dim\ndef fourier_domain_adaptation(img: np.ndarray, target_img: np.ndarray, beta: float) -> np.ndarray:\n    \"\"\"Apply Fourier Domain Adaptation to the input image using a target image.\n\n    This function performs domain adaptation in the frequency domain by modifying the amplitude\n    spectrum of the source image based on the target image's amplitude spectrum. It preserves\n    the phase information of the source image, which helps maintain its content while adapting\n    its style to match the target image.\n\n    Args:\n        img (np.ndarray): The source image to be adapted. Can be grayscale or RGB.\n        target_img (np.ndarray): The target image used as a reference for adaptation.\n            Should have the same dimensions as the source image.\n        beta (float): The adaptation strength, typically in the range [0, 1].\n            Higher values result in stronger adaptation towards the target image's style.\n\n    Returns:\n        np.ndarray: The adapted image with the same shape and type as the input image.\n\n    Raises:\n        ValueError: If the source and target images have different shapes.\n\n    Note:\n        - Both input images are converted to float32 for processing.\n        - The function handles both grayscale (2D) and color (3D) images.\n        - For grayscale images, an extra dimension is added to facilitate uniform processing.\n        - The adaptation is performed channel-wise for color images.\n        - The output is clipped to the valid range and preserves the original number of channels.\n\n    The adaptation process involves the following steps for each channel:\n    1. Compute the 2D Fourier Transform of both source and target images.\n    2. Shift the zero frequency component to the center of the spectrum.\n    3. Extract amplitude and phase information from the source image's spectrum.\n    4. Mutate the source amplitude using the target amplitude and the beta parameter.\n    5. Combine the mutated amplitude with the original phase.\n    6. Perform the inverse Fourier Transform to obtain the adapted channel.\n\n    The `low_freq_mutate` function (not shown here) is responsible for the actual\n    amplitude mutation, focusing on low-frequency components which carry style information.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> source_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> target_img = np.random.rand(100, 100, 3).astype(np.float32)\n        >>> adapted_img = A.fourier_domain_adaptation(source_img, target_img, beta=0.5)\n        >>> assert adapted_img.shape == source_img.shape\n\n    References:\n        - \"FDA: Fourier Domain Adaptation for Semantic Segmentation\"\n          (Yang and Soatto, 2020, CVPR)\n          https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n    \"\"\"\n    src_img = img.astype(np.float32)\n    trg_img = target_img.astype(np.float32)\n\n    if src_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        src_img = np.expand_dims(src_img, axis=-1)\n    if trg_img.ndim == MONO_CHANNEL_DIMENSIONS:\n        trg_img = np.expand_dims(trg_img, axis=-1)\n\n    num_channels = src_img.shape[-1]\n\n    # Prepare container for the output image\n    src_in_trg = np.zeros_like(src_img)\n\n    for channel_id in range(num_channels):\n        # Perform FFT on each channel\n        fft_src = np.fft.fft2(src_img[:, :, channel_id])\n        fft_trg = np.fft.fft2(trg_img[:, :, channel_id])\n\n        # Shift the zero frequency component to the center\n        fft_src_shifted = np.fft.fftshift(fft_src)\n        fft_trg_shifted = np.fft.fftshift(fft_trg)\n\n        # Extract amplitude and phase\n        amp_src, pha_src = np.abs(fft_src_shifted), np.angle(fft_src_shifted)\n        amp_trg = np.abs(fft_trg_shifted)\n\n        # Mutate the amplitude part of the source with the target\n        mutated_amp = low_freq_mutate(amp_src.copy(), amp_trg, beta)\n\n        # Combine the mutated amplitude with the original phase\n        fft_src_mutated = np.fft.ifftshift(mutated_amp * np.exp(1j * pha_src))\n\n        # Perform inverse FFT\n        src_in_trg_channel = np.fft.ifft2(fft_src_mutated)\n\n        # Store the result in the corresponding channel of the output image\n        src_in_trg[:, :, channel_id] = np.real(src_in_trg_channel)\n\n    return src_in_trg\n
"},{"location":"api_reference/augmentations/domain_adaptation/functional/#albumentations.augmentations.domain_adaptation.functional.match_histograms","title":"def match_histograms (image, reference) [view source on GitHub]","text":"

Adjust an image so that its cumulative histogram matches that of another.

The adjustment is applied separately for each channel.

Parameters:

Name Type Description image np.ndarray

Input image. Can be gray-scale or in color.

reference np.ndarray

Image to match histogram of. Must have the same number of channels as image.

channel_axis

If None, the image is assumed to be a grayscale (single channel) image. Otherwise, this parameter indicates which axis of the array corresponds to channels.

Returns:

Type Description np.ndarray

Transformed input image.

Exceptions:

Type Description ValueError

Thrown when the number of channels in the input image and the reference differ.

Source code in albumentations/augmentations/domain_adaptation/functional.py Python
@uint8_io\n@preserve_channel_dim\ndef match_histograms(image: np.ndarray, reference: np.ndarray) -> np.ndarray:\n    \"\"\"Adjust an image so that its cumulative histogram matches that of another.\n\n    The adjustment is applied separately for each channel.\n\n    Args:\n        image: Input image. Can be gray-scale or in color.\n        reference: Image to match histogram of. Must have the same number of channels as image.\n        channel_axis: If None, the image is assumed to be a grayscale (single channel) image.\n            Otherwise, this parameter indicates which axis of the array corresponds to channels.\n\n    Returns:\n        np.ndarray: Transformed input image.\n\n    Raises:\n        ValueError: Thrown when the number of channels in the input image and the reference differ.\n    \"\"\"\n    if reference.dtype != np.uint8:\n        reference = from_float(reference, np.uint8)\n\n    if image.ndim != reference.ndim:\n        raise ValueError(\"Image and reference must have the same number of dimensions.\")\n\n    # Expand dimensions for grayscale images\n    if image.ndim == 2:\n        image = np.expand_dims(image, axis=-1)\n    if reference.ndim == 2:\n        reference = np.expand_dims(reference, axis=-1)\n\n    matched = np.empty(image.shape, dtype=np.uint8)\n\n    num_channels = image.shape[-1]\n\n    for channel in range(num_channels):\n        matched_channel = _match_cumulative_cdf(image[..., channel], reference[..., channel]).astype(np.uint8)\n        matched[..., channel] = matched_channel\n\n    return matched\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/","title":"Domain Adaptation transforms (augmentations.domain_adaptation.transforms)","text":""},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.FDA","title":"class FDA (reference_images, beta_limit=(0, 0.1), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source and target datasets, effectively adapting images from one domain to closely resemble those from another without altering their semantic content.

This transform is particularly beneficial in scenarios where the training (source) and testing (target) images come from different distributions, such as synthetic versus real images, or day versus night scenes. Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain alignment by swapping low-frequency components of the Fourier transform between the source and target images. This technique has shown to improve the performance of models on the target domain, particularly for tasks like semantic segmentation, without additional training for domain invariance.

The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more of the original image's characteristics and higher values leading to more pronounced adaptation effects. It is recommended to use beta values less than 0.3 to avoid introducing artifacts.

Parameters:

Name Type Description reference_images Sequence[Any]

Sequence of objects to be converted into images by read_fn. This typically involves paths to images that serve as target domain examples for adaptation.

beta_limit tuple[float, float] | float

Coefficient beta from the paper, controlling the swapping extent of frequency components. If one value is provided beta will be sampled from uniform distribution [0, beta_limit]. Values should be less than 0.5.

read_fn Callable

User-defined function for reading images. It takes an element from reference_images and returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a numpy array.

Targets

image

Image types: uint8, float32

Reference

  • https://github.com/YanchaoYang/FDA
  • https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n>>> result = aug(image=image)\n

Note

FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target domain samples are unavailable. It enables significant improvements in model generalization by aligning the low-level statistics of source and target images through a simple yet effective Fourier-based method.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class FDA(ImageOnlyTransform):\n    \"\"\"Fourier Domain Adaptation (FDA) for simple \"style transfer\" in the context of unsupervised domain adaptation\n    (UDA). FDA manipulates the frequency components of images to reduce the domain gap between source\n    and target datasets, effectively adapting images from one domain to closely resemble those from another without\n    altering their semantic content.\n\n    This transform is particularly beneficial in scenarios where the training (source) and testing (target) images\n    come from different distributions, such as synthetic versus real images, or day versus night scenes.\n    Unlike traditional domain adaptation methods that may require complex adversarial training, FDA achieves domain\n    alignment by swapping low-frequency components of the Fourier transform between the source and target images.\n    This technique has shown to improve the performance of models on the target domain, particularly for tasks\n    like semantic segmentation, without additional training for domain invariance.\n\n    The 'beta_limit' parameter controls the extent of frequency component swapping, with lower values preserving more\n    of the original image's characteristics and higher values leading to more pronounced adaptation effects.\n    It is recommended to use beta values less than 0.3 to avoid introducing artifacts.\n\n    Args:\n        reference_images (Sequence[Any]): Sequence of objects to be converted into images by `read_fn`. This typically\n            involves paths to images that serve as target domain examples for adaptation.\n        beta_limit (tuple[float, float] | float): Coefficient beta from the paper, controlling the swapping extent of\n            frequency components. If one value is provided beta will be sampled from uniform\n            distribution [0, beta_limit]. Values should be less than 0.5.\n        read_fn (Callable): User-defined function for reading images. It takes an element from `reference_images` and\n            returns a numpy array of image pixels. By default, it is expected to take a path to an image and return a\n            numpy array.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        - https://github.com/YanchaoYang/FDA\n        - https://openaccess.thecvf.com/content_CVPR_2020/papers/Yang_FDA_Fourier_Domain_Adaptation_for_Semantic_Segmentation_CVPR_2020_paper.pdf\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> target_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> aug = A.Compose([A.FDA([target_image], p=1, read_fn=lambda x: x)])\n        >>> result = aug(image=image)\n\n    Note:\n        FDA is a powerful tool for domain adaptation, particularly in unsupervised settings where annotated target\n        domain samples are unavailable. It enables significant improvements in model generalization by aligning\n        the low-level statistics of source and target images through a simple yet effective Fourier-based method.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        read_fn: Callable[[Any], np.ndarray]\n        beta_limit: ZeroOneRangeType\n\n        @field_validator(\"beta_limit\")\n        @classmethod\n        def check_ranges(cls, value: tuple[float, float]) -> tuple[float, float]:\n            bounds = 0, MAX_BETA_LIMIT\n            if not bounds[0] <= value[0] <= value[1] <= bounds[1]:\n                raise ValueError(f\"Values should be in the range {bounds} got {value} \")\n            return value\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        beta_limit: ScaleFloatType = (0, 0.1),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.beta_limit = cast(tuple[float, float], beta_limit)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        target_image: np.ndarray,\n        beta: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fourier_domain_adaptation(img, target_image, beta)\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, np.ndarray]:\n        height, width = params[\"shape\"][:2]\n        target_img = self.read_fn(self.py_random.choice(self.reference_images))\n        target_img = cv2.resize(target_img, dsize=(width, height))\n\n        return {\"target_image\": target_img, \"beta\": self.py_random.uniform(*self.beta_limit)}\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str]:\n        return \"reference_images\", \"beta_limit\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"FDA can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.HistogramMatching","title":"class HistogramMatching (reference_images, blend_ratio=(0.5, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, p=0.5, always_apply=None) [view source on GitHub]","text":"

Adjust the pixel values of an input image to match the histogram of a reference image.

This transform applies histogram matching, a technique that modifies the distribution of pixel intensities in the input image to closely resemble that of a reference image. This process is performed independently for each channel in multi-channel images, provided both the input and reference images have the same number of channels.

Histogram matching is particularly useful for: - Normalizing images from different sources or captured under varying conditions. - Preparing images for feature matching or other computer vision tasks where consistent tone and contrast are important. - Simulating different lighting or camera conditions in a controlled manner.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of reference image sources. These can be file paths, URLs, or any objects that can be converted to images by the read_fn.

blend_ratio tuple[float, float]

Range for the blending factor between the original and the matched image. Must be two floats between 0 and 1, where: - 0 means no blending (original image is returned) - 1 means full histogram matching A random value within this range is chosen for each application. Default: (0.5, 1.0)

read_fn Callable[[Any], np.ndarray]

A function that takes an element from reference_images and returns a numpy array representing the image. Default: read_rgb_image (reads image file from disk)

p float

Probability of applying the transform. Default: 0.5

Targets

image

Image types: uint8, float32

Note

  • This transform cannot be directly serialized due to its dependency on external image data.
  • The effectiveness of the matching depends on the similarity between the input and reference images.
  • For best results, choose reference images that represent the desired tone and contrast.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.HistogramMatching(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     read_fn=lambda x: x,\n...     p=1\n... )\n>>> result = transform(image=image)\n>>> matched_image = result[\"image\"]\n

References

  • Histogram Matching in scikit-image: https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class HistogramMatching(ImageOnlyTransform):\n    \"\"\"Adjust the pixel values of an input image to match the histogram of a reference image.\n\n    This transform applies histogram matching, a technique that modifies the distribution of pixel\n    intensities in the input image to closely resemble that of a reference image. This process is\n    performed independently for each channel in multi-channel images, provided both the input and\n    reference images have the same number of channels.\n\n    Histogram matching is particularly useful for:\n    - Normalizing images from different sources or captured under varying conditions.\n    - Preparing images for feature matching or other computer vision tasks where consistent\n      tone and contrast are important.\n    - Simulating different lighting or camera conditions in a controlled manner.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of reference image sources. These can be\n            file paths, URLs, or any objects that can be converted to images by the `read_fn`.\n        blend_ratio (tuple[float, float]): Range for the blending factor between the original\n            and the matched image. Must be two floats between 0 and 1, where:\n            - 0 means no blending (original image is returned)\n            - 1 means full histogram matching\n            A random value within this range is chosen for each application.\n            Default: (0.5, 1.0)\n        read_fn (Callable[[Any], np.ndarray]): A function that takes an element from\n            `reference_images` and returns a numpy array representing the image.\n            Default: read_rgb_image (reads image file from disk)\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform cannot be directly serialized due to its dependency on external image data.\n        - The effectiveness of the matching depends on the similarity between the input and reference images.\n        - For best results, choose reference images that represent the desired tone and contrast.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.HistogramMatching(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     read_fn=lambda x: x,\n        ...     p=1\n        ... )\n        >>> result = transform(image=image)\n        >>> matched_image = result[\"image\"]\n\n    References:\n        - Histogram Matching in scikit-image:\n          https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_histogram_matching.html\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.5, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n\n    def apply(\n        self: np.ndarray,\n        img: np.ndarray,\n        reference_image: np.ndarray,\n        blend_ratio: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return apply_histogram(img, reference_image, blend_ratio)\n\n    def get_params(self) -> dict[str, np.ndarray]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"HistogramMatching can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.PixelDistributionAdaptation","title":"class PixelDistributionAdaptation (reference_images, blend_ratio=(0.25, 1.0), read_fn=<function read_rgb_image at 0x7fcff8b62f20>, transform_type='pca', p=0.5, always_apply=None) [view source on GitHub]","text":"

Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image with that of a reference image. This process involves fitting a simple statistical transformation (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images, transforming the original image with the transformation trained on it, and then applying the inverse transformation using the transform fitted on the reference image. The result is an adapted image that retains the original content while mimicking the pixel value distribution of the reference domain.

The process can be visualized as two main steps: 1. Adjusting the original image to a standard distribution space using a selected transform. 2. Moving the adjusted image into the distribution space of the reference image by applying the inverse of the transform fitted on the reference image.

This technique is especially useful in scenarios where images from different domains (e.g., synthetic vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in image processing tasks.

Parameters:

Name Type Description reference_images Sequence[Any]

A sequence of objects (typically image paths) that will be converted into images by read_fn. These images serve as references for the domain adaptation.

blend_ratio tuple[float, float]

Specifies the minimum and maximum blend ratio for mixing the adapted image with the original. This enhances the diversity of the output images. Values should be in the range [0, 1]. Default: (0.25, 1.0)

read_fn Callable

A user-defined function for reading and converting the objects in reference_images into numpy arrays. By default, it assumes these objects are image paths.

transform_type Literal[\"pca\", \"standard\", \"minmax\"]

Specifies the type of statistical transformation to apply. - \"pca\": Principal Component Analysis - \"standard\": StandardScaler (zero mean and unit variance) - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1]) Default: \"pca\"

p float

The probability of applying the transform to any given image. Default: 0.5

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The effectiveness of the adaptation depends on the similarity between the input and reference domains.
  • PCA transformation may alter color relationships more significantly than other methods.
  • StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.
  • The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.
  • This transform cannot be directly serialized due to its dependency on external image data.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n>>> transform = A.PixelDistributionAdaptation(\n...     reference_images=[reference_image],\n...     blend_ratio=(0.5, 1.0),\n...     transform_type=\"standard\",\n...     read_fn=lambda x: x,\n...     p=1.0\n... )\n>>> result = transform(image=image)\n>>> adapted_image = result[\"image\"]\n

References

  • https://github.com/arsenyinfo/qudida
  • https://arxiv.org/abs/1911.11483

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class PixelDistributionAdaptation(ImageOnlyTransform):\n    \"\"\"Performs pixel-level domain adaptation by aligning the pixel value distribution of an input image\n    with that of a reference image. This process involves fitting a simple statistical transformation\n    (such as PCA, StandardScaler, or MinMaxScaler) to both the original and the reference images,\n    transforming the original image with the transformation trained on it, and then applying the inverse\n    transformation using the transform fitted on the reference image. The result is an adapted image\n    that retains the original content while mimicking the pixel value distribution of the reference domain.\n\n    The process can be visualized as two main steps:\n    1. Adjusting the original image to a standard distribution space using a selected transform.\n    2. Moving the adjusted image into the distribution space of the reference image by applying the inverse\n       of the transform fitted on the reference image.\n\n    This technique is especially useful in scenarios where images from different domains (e.g., synthetic\n    vs. real images, day vs. night scenes) need to be harmonized for better consistency or performance in\n    image processing tasks.\n\n    Args:\n        reference_images (Sequence[Any]): A sequence of objects (typically image paths) that will be\n            converted into images by `read_fn`. These images serve as references for the domain adaptation.\n        blend_ratio (tuple[float, float]): Specifies the minimum and maximum blend ratio for mixing\n            the adapted image with the original. This enhances the diversity of the output images.\n            Values should be in the range [0, 1]. Default: (0.25, 1.0)\n        read_fn (Callable): A user-defined function for reading and converting the objects in\n            `reference_images` into numpy arrays. By default, it assumes these objects are image paths.\n        transform_type (Literal[\"pca\", \"standard\", \"minmax\"]): Specifies the type of statistical\n            transformation to apply.\n            - \"pca\": Principal Component Analysis\n            - \"standard\": StandardScaler (zero mean and unit variance)\n            - \"minmax\": MinMaxScaler (scales to a fixed range, usually [0, 1])\n            Default: \"pca\"\n        p (float): The probability of applying the transform to any given image. Default: 0.5\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The effectiveness of the adaptation depends on the similarity between the input and reference domains.\n        - PCA transformation may alter color relationships more significantly than other methods.\n        - StandardScaler and MinMaxScaler preserve color relationships better but may provide less dramatic adaptations.\n        - The blend_ratio parameter allows for a smooth transition between the original and fully adapted image.\n        - This transform cannot be directly serialized due to its dependency on external image data.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> reference_image = np.random.randint(0, 256, [100, 100, 3], dtype=np.uint8)\n        >>> transform = A.PixelDistributionAdaptation(\n        ...     reference_images=[reference_image],\n        ...     blend_ratio=(0.5, 1.0),\n        ...     transform_type=\"standard\",\n        ...     read_fn=lambda x: x,\n        ...     p=1.0\n        ... )\n        >>> result = transform(image=image)\n        >>> adapted_image = result[\"image\"]\n\n    References:\n        - https://github.com/arsenyinfo/qudida\n        - https://arxiv.org/abs/1911.11483\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        reference_images: Sequence[Any]\n        blend_ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, 1)),\n        ]\n        read_fn: Callable[[Any], np.ndarray]\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"]\n\n    def __init__(\n        self,\n        reference_images: Sequence[Any],\n        blend_ratio: tuple[float, float] = (0.25, 1.0),\n        read_fn: Callable[[Any], np.ndarray] = read_rgb_image,\n        transform_type: Literal[\"pca\", \"standard\", \"minmax\"] = \"pca\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.reference_images = reference_images\n        self.read_fn = read_fn\n        self.blend_ratio = blend_ratio\n        self.transform_type = transform_type\n\n    def apply(self, img: np.ndarray, reference_image: np.ndarray, blend_ratio: float, **params: Any) -> np.ndarray:\n        return adapt_pixel_distribution(\n            img,\n            ref=reference_image,\n            weight=blend_ratio,\n            transform_type=self.transform_type,\n        )\n\n    def get_params(self) -> dict[str, Any]:\n        return {\n            \"reference_image\": self.read_fn(self.py_random.choice(self.reference_images)),\n            \"blend_ratio\": self.py_random.uniform(*self.blend_ratio),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, str, str, str]:\n        return \"reference_images\", \"blend_ratio\", \"read_fn\", \"transform_type\"\n\n    def to_dict_private(self) -> dict[str, Any]:\n        msg = \"PixelDistributionAdaptation can not be serialized.\"\n        raise NotImplementedError(msg)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms.TemplateTransform","title":"class TemplateTransform (templates, img_weight=(0.5, 0.5), template_weight=None, template_transform=None, name=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply blending of input image with specified templates.

This transform overlays one or more template images onto the input image using alpha blending. It allows for creating complex composite images or simulating various visual effects.

Parameters:

Name Type Description templates numpy array | list[np.ndarray]

Images to use as templates for the transform. If a single numpy array is provided, it will be used as the only template. If a list of numpy arrays is provided, one will be randomly chosen for each application.

img_weight tuple[float, float] | float

Weight of the original image in the blend. If a single float, that value will always be used. If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application. To use a fixed weight, use (weight, weight). Default: (0.5, 0.5).

template_transform A.Compose | None

A composition of Albumentations transforms to apply to the template before blending. This should be an instance of A.Compose containing one or more Albumentations transforms. Default: None.

name str | None

Name of the transform instance. Used for serialization purposes. Default: None.

p float

Probability of applying the transform. Default: 0.5.

Targets

image

Image types: uint8, float32

Number of channels: Any

Note

  • The template(s) must have the same number of channels as the input image or be single-channel.
  • If a single-channel template is used with a multi-channel image, the template will be replicated across all channels.
  • The template(s) will be resized to match the input image size if they differ.
  • To make this transform serializable, provide a name when initializing it.

Mathematical Formulation: Given: - I: Input image - T: Template image - w_i: Weight of input image (sampled from img_weight)

The blended image B is computed as:\n\nB = w_i * I + (1 - w_i) * T\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-a-single-template","title":"Apply template transform with a single template","text":"Python
>>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-multiple-templates-and-custom-weights","title":"Apply template transform with multiple templates and custom weights","text":"Python
>>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n>>> transform = A.TemplateTransform(\n...     templates=templates,\n...     img_weight=(0.3, 0.7),\n...     name=\"multi_template_transform\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n
"},{"location":"api_reference/augmentations/domain_adaptation/transforms/#albumentations.augmentations.domain_adaptation.transforms--apply-template-transform-with-additional-transforms-on-the-template","title":"Apply template transform with additional transforms on the template","text":"Python
>>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n>>> transform = A.TemplateTransform(\n...     templates=template,\n...     img_weight=0.6,\n...     template_transform=template_transform,\n...     name=\"transformed_template\",\n...     p=1.0\n... )\n>>> blended_image = transform(image=image)['image']\n

References

  • Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing
  • Image blending: https://en.wikipedia.org/wiki/Image_blending

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/domain_adaptation/transforms.py Python
class TemplateTransform(ImageOnlyTransform):\n    \"\"\"Apply blending of input image with specified templates.\n\n    This transform overlays one or more template images onto the input image using alpha blending.\n    It allows for creating complex composite images or simulating various visual effects.\n\n    Args:\n        templates (numpy array | list[np.ndarray]): Images to use as templates for the transform.\n            If a single numpy array is provided, it will be used as the only template.\n            If a list of numpy arrays is provided, one will be randomly chosen for each application.\n\n        img_weight (tuple[float, float]  | float): Weight of the original image in the blend.\n            If a single float, that value will always be used.\n            If a tuple (min, max), the weight will be randomly sampled from the range [min, max) for each application.\n            To use a fixed weight, use (weight, weight).\n            Default: (0.5, 0.5).\n\n        template_transform (A.Compose | None): A composition of Albumentations transforms to apply to the template\n            before blending.\n            This should be an instance of A.Compose containing one or more Albumentations transforms.\n            Default: None.\n\n        name (str | None): Name of the transform instance. Used for serialization purposes.\n            Default: None.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image\n\n    Image types:\n        uint8, float32\n\n    Number of channels:\n        Any\n\n    Note:\n        - The template(s) must have the same number of channels as the input image or be single-channel.\n        - If a single-channel template is used with a multi-channel image, the template will be replicated across\n          all channels.\n        - The template(s) will be resized to match the input image size if they differ.\n        - To make this transform serializable, provide a name when initializing it.\n\n    Mathematical Formulation:\n        Given:\n        - I: Input image\n        - T: Template image\n        - w_i: Weight of input image (sampled from img_weight)\n\n        The blended image B is computed as:\n\n        B = w_i * I + (1 - w_i) * T\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> template = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n\n        # Apply template transform with a single template\n        >>> transform = A.TemplateTransform(templates=template, name=\"my_template_transform\", p=1.0)\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with multiple templates and custom weights\n        >>> templates = [np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8) for _ in range(3)]\n        >>> transform = A.TemplateTransform(\n        ...     templates=templates,\n        ...     img_weight=(0.3, 0.7),\n        ...     name=\"multi_template_transform\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n        # Apply template transform with additional transforms on the template\n        >>> template_transform = A.Compose([A.RandomBrightnessContrast(p=1)])\n        >>> transform = A.TemplateTransform(\n        ...     templates=template,\n        ...     img_weight=0.6,\n        ...     template_transform=template_transform,\n        ...     name=\"transformed_template\",\n        ...     p=1.0\n        ... )\n        >>> blended_image = transform(image=image)['image']\n\n    References:\n        - Alpha compositing: https://en.wikipedia.org/wiki/Alpha_compositing\n        - Image blending: https://en.wikipedia.org/wiki/Image_blending\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        templates: np.ndarray | Sequence[np.ndarray]\n        img_weight: ZeroOneRangeType\n        template_weight: ZeroOneRangeType | None = Field(\n            deprecated=\"Template_weight is deprecated. Computed automatically as (1 - img_weight)\",\n        )\n        template_transform: Compose | BasicTransform | None = None\n        name: str | None\n\n        @field_validator(\"templates\")\n        @classmethod\n        def validate_templates(cls, v: np.ndarray | list[np.ndarray]) -> list[np.ndarray]:\n            if isinstance(v, np.ndarray):\n                return [v]\n            if isinstance(v, list):\n                if not all(isinstance(item, np.ndarray) for item in v):\n                    msg = \"All templates must be numpy arrays.\"\n                    raise ValueError(msg)\n                return v\n            msg = \"Templates must be a numpy array or a list of numpy arrays.\"\n            raise TypeError(msg)\n\n    def __init__(\n        self,\n        templates: np.ndarray | list[np.ndarray],\n        img_weight: ScaleFloatType = (0.5, 0.5),\n        template_weight: None = None,\n        template_transform: Compose | BasicTransform | None = None,\n        name: str | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.templates = templates\n        self.img_weight = cast(tuple[float, float], img_weight)\n        self.template_transform = template_transform\n        self.name = name\n\n    def apply(\n        self,\n        img: np.ndarray,\n        template: np.ndarray,\n        img_weight: float,\n        **params: Any,\n    ) -> np.ndarray:\n        if img_weight == 0:\n            return template\n        if img_weight == 1:\n            return img\n\n        return add_weighted(img, img_weight, template, 1 - img_weight)\n\n    def get_params(self) -> dict[str, float]:\n        return {\n            \"img_weight\": self.py_random.uniform(*self.img_weight),\n        }\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        template = self.py_random.choice(self.templates)\n\n        if self.template_transform is not None:\n            template = self.template_transform(image=template)[\"image\"]\n\n        if get_num_channels(template) not in [1, get_num_channels(image)]:\n            msg = (\n                \"Template must be a single channel or \"\n                \"has the same number of channels as input \"\n                f\"image ({get_num_channels(image)}), got {get_num_channels(template)}\"\n            )\n            raise ValueError(msg)\n\n        if template.dtype != image.dtype:\n            msg = \"Image and template must be the same image type\"\n            raise ValueError(msg)\n\n        if image.shape[:2] != template.shape[:2]:\n            template = fgeometric.resize(template, image.shape[:2], interpolation=cv2.INTER_AREA)\n\n        if get_num_channels(template) == 1 and get_num_channels(image) > 1:\n            # Replicate single channel template across all channels to match input image\n            template = cv2.merge([template] * get_num_channels(image))\n        # in order to support grayscale image with dummy dim\n        template = template.reshape(image.shape)\n\n        return {\"template\": template}\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    def to_dict_private(self) -> dict[str, Any]:\n        if self.name is None:\n            msg = (\n                \"To make a TemplateTransform serializable you should provide the `name` argument, \"\n                \"e.g. `TemplateTransform(name='my_transform', ...)`.\"\n            )\n            raise ValueError(msg)\n        return {\"__class_fullname__\": self.get_class_fullname(), \"__name__\": self.name}\n
"},{"location":"api_reference/augmentations/dropout/","title":"Index","text":"
  • ChannelDropout augmentation (albumentations.augmentations.dropout.channel_dropout)
  • CoarseDropout augmentation (albumentations.augmentations.dropout.coarse_dropout)
  • GridDropout augmentation (albumentations.augmentations.dropout.grid_dropout)
  • MaskDropout augmentation (albumentations.augmentations.dropout.mask_dropout)
"},{"location":"api_reference/augmentations/dropout/channel_dropout/","title":"ChannelDropout augmentation (augmentations.dropout.channel_dropout)","text":""},{"location":"api_reference/augmentations/dropout/channel_dropout/#albumentations.augmentations.dropout.channel_dropout.ChannelDropout","title":"class ChannelDropout (channel_drop_range=(1, 1), fill_value=None, fill=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly drop channels in the input image.

This transform randomly selects a number of channels to drop from the input image and replaces them with a specified fill value. This can improve model robustness to missing or corrupted channels.

The technique is conceptually similar to: - Dropout layers in neural networks, which randomly set input units to 0 during training. - CoarseDropout augmentation, which drops out regions in the spatial dimensions of the image.

However, ChannelDropout operates on the channel dimension, effectively \"dropping out\" entire color channels or feature maps.

Parameters:

Name Type Description channel_drop_range tuple[int, int]

Range from which to choose the number of channels to drop. The actual number will be randomly selected from the inclusive range [min, max]. Default: (1, 1).

fill float

Pixel value used to fill the dropped channels. Default: 0.

p float

Probability of applying the transform. Must be in the range [0, 1]. Default: 0.5.

Exceptions:

Type Description NotImplementedError

If the input image has only one channel.

ValueError

If the upper bound of channel_drop_range is greater than or equal to the number of channels in the input image.

Targets

image, volume

Image types: uint8, float32

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.ChannelDropout(channel_drop_range=(1, 2), fill=128, p=1.0)\n>>> result = transform(image=image)\n>>> dropped_image = result['image']\n>>> assert dropped_image.shape == image.shape\n>>> assert np.any(dropped_image != image)  # Some channels should be different\n

Note

  • The number of channels to drop is randomly chosen within the specified range.
  • Channels are randomly selected for dropping.
  • This transform is not applicable to single-channel (grayscale) images.
  • The transform will raise an error if it's not possible to drop the specified number of channels (e.g., trying to drop 3 channels from an RGB image).
  • This augmentation can be particularly useful for training models to be robust against missing or corrupted channel data in multi-spectral or hyperspectral imagery.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/channel_dropout.py Python
class ChannelDropout(ImageOnlyTransform):\n    \"\"\"Randomly drop channels in the input image.\n\n    This transform randomly selects a number of channels to drop from the input image\n    and replaces them with a specified fill value. This can improve model robustness\n    to missing or corrupted channels.\n\n    The technique is conceptually similar to:\n    - Dropout layers in neural networks, which randomly set input units to 0 during training.\n    - CoarseDropout augmentation, which drops out regions in the spatial dimensions of the image.\n\n    However, ChannelDropout operates on the channel dimension, effectively \"dropping out\"\n    entire color channels or feature maps.\n\n    Args:\n        channel_drop_range (tuple[int, int]): Range from which to choose the number\n            of channels to drop. The actual number will be randomly selected from\n            the inclusive range [min, max]. Default: (1, 1).\n        fill (float): Pixel value used to fill the dropped channels.\n            Default: 0.\n        p (float): Probability of applying the transform. Must be in the range\n            [0, 1]. Default: 0.5.\n\n    Raises:\n        NotImplementedError: If the input image has only one channel.\n        ValueError: If the upper bound of channel_drop_range is greater than or\n            equal to the number of channels in the input image.\n\n    Targets:\n        image, volume\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.ChannelDropout(channel_drop_range=(1, 2), fill=128, p=1.0)\n        >>> result = transform(image=image)\n        >>> dropped_image = result['image']\n        >>> assert dropped_image.shape == image.shape\n        >>> assert np.any(dropped_image != image)  # Some channels should be different\n\n    Note:\n        - The number of channels to drop is randomly chosen within the specified range.\n        - Channels are randomly selected for dropping.\n        - This transform is not applicable to single-channel (grayscale) images.\n        - The transform will raise an error if it's not possible to drop the specified\n          number of channels (e.g., trying to drop 3 channels from an RGB image).\n        - This augmentation can be particularly useful for training models to be robust\n          against missing or corrupted channel data in multi-spectral or hyperspectral imagery.\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        channel_drop_range: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        fill_value: float | None\n        fill: float\n\n        @model_validator(mode=\"after\")\n        def validate_fill(self) -> Self:\n            if self.fill_value is not None:\n                self.fill = self.fill_value\n                warn(\n                    \"`fill_value` deprecated. Use `fill` instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n            return self\n\n    def __init__(\n        self,\n        channel_drop_range: tuple[int, int] = (1, 1),\n        fill_value: float | None = None,\n        fill: float = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.channel_drop_range = channel_drop_range\n        self.fill = fill\n\n    def apply(self, img: np.ndarray, channels_to_drop: tuple[int, ...], **params: Any) -> np.ndarray:\n        return channel_dropout(img, channels_to_drop, self.fill)\n\n    def get_params_dependent_on_data(self, params: Mapping[str, Any], data: Mapping[str, Any]) -> dict[str, Any]:\n        image = data[\"image\"] if \"image\" in data else data[\"images\"][0]\n\n        num_channels = get_num_channels(image)\n\n        if num_channels == 1:\n            msg = \"Images has one channel. ChannelDropout is not defined.\"\n            raise NotImplementedError(msg)\n\n        if self.channel_drop_range[1] >= num_channels:\n            msg = \"Can not drop all channels in ChannelDropout.\"\n            raise ValueError(msg)\n\n        num_drop_channels = self.py_random.randint(*self.channel_drop_range)\n\n        channels_to_drop = self.py_random.sample(range(num_channels), k=num_drop_channels)\n\n        return {\"channels_to_drop\": channels_to_drop}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"channel_drop_range\", \"fill\"\n
"},{"location":"api_reference/augmentations/dropout/coarse_dropout/","title":"CoarseDropout augmentation (augmentations.dropout.coarse_dropout)","text":""},{"location":"api_reference/augmentations/dropout/coarse_dropout/#albumentations.augmentations.dropout.coarse_dropout.CoarseDropout","title":"class CoarseDropout (max_holes=None, max_height=None, max_width=None, min_holes=None, min_height=None, min_width=None, fill_value=None, mask_fill_value=None, num_holes_range=(1, 1), hole_height_range=(8, 8), hole_width_range=(8, 8), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

CoarseDropout randomly drops out rectangular regions from the image and optionally, the corresponding regions in an associated mask, to simulate occlusion and varied object sizes found in real-world settings.

This transformation is an evolution of CutOut and RandomErasing, offering more flexibility in the size, number of dropout regions, and fill values.

Parameters:

Name Type Description num_holes_range tuple[int, int]

Range (min, max) for the number of rectangular regions to drop out. Default: (1, 1)

hole_height_range tuple[Real, Real]

Range (min, max) for the height of dropout regions. If int, specifies absolute pixel values. If float, interpreted as a fraction of the image height. Default: (8, 8)

hole_width_range tuple[Real, Real]

Range (min, max) for the width of dropout regions. If int, specifies absolute pixel values. If float, interpreted as a fraction of the image width. Default: (8, 8)

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the mask. If None, mask regions corresponding to image dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges for each application.
  • When using float values for hole_height_range and hole_width_range, ensure they are between 0 and 1.
  • This implementation includes deprecation warnings for older parameter names (min_holes, max_holes, etc.).
  • Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.
  • For 'random_uniform' fill, each hole gets a single random color, unlike 'random' where each pixel gets its own random value.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> # Example with random uniform fill\n>>> aug_random = A.CoarseDropout(\n...     num_holes_range=(3, 6),\n...     hole_height_range=(10, 20),\n...     hole_width_range=(10, 20),\n...     fill=\"random_uniform\",\n...     p=1.0\n... )\n>>> # Example with inpainting\n>>> aug_inpaint = A.CoarseDropout(\n...     num_holes_range=(3, 6),\n...     hole_height_range=(10, 20),\n...     hole_width_range=(10, 20),\n...     fill=\"inpaint_ns\",\n...     p=1.0\n... )\n>>> transformed = aug_random(image=image, mask=mask)\n>>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n

References

  • CutOut: https://arxiv.org/abs/1708.04552
  • Random Erasing: https://arxiv.org/abs/1708.04896
  • OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/coarse_dropout.py Python
class CoarseDropout(BaseDropout):\n    \"\"\"CoarseDropout randomly drops out rectangular regions from the image and optionally,\n    the corresponding regions in an associated mask, to simulate occlusion and\n    varied object sizes found in real-world settings.\n\n    This transformation is an evolution of CutOut and RandomErasing, offering more\n    flexibility in the size, number of dropout regions, and fill values.\n\n    Args:\n        num_holes_range (tuple[int, int]): Range (min, max) for the number of rectangular\n            regions to drop out. Default: (1, 1)\n        hole_height_range (tuple[Real, Real]): Range (min, max) for the height\n            of dropout regions. If int, specifies absolute pixel values. If float,\n            interpreted as a fraction of the image height. Default: (8, 8)\n        hole_width_range (tuple[Real, Real]): Range (min, max) for the width\n            of dropout regions. If int, specifies absolute pixel values. If float,\n            interpreted as a fraction of the image width. Default: (8, 8)\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        fill_mask (ColorType | None): Fill value for dropout regions in the mask.\n            If None, mask regions corresponding to image dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual number and size of dropout regions are randomly chosen within the specified ranges for each\n            application.\n        - When using float values for hole_height_range and hole_width_range, ensure they are between 0 and 1.\n        - This implementation includes deprecation warnings for older parameter names (min_holes, max_holes, etc.).\n        - Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.\n        - For 'random_uniform' fill, each hole gets a single random color, unlike 'random' where each pixel\n            gets its own random value.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> # Example with random uniform fill\n        >>> aug_random = A.CoarseDropout(\n        ...     num_holes_range=(3, 6),\n        ...     hole_height_range=(10, 20),\n        ...     hole_width_range=(10, 20),\n        ...     fill=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> # Example with inpainting\n        >>> aug_inpaint = A.CoarseDropout(\n        ...     num_holes_range=(3, 6),\n        ...     hole_height_range=(10, 20),\n        ...     hole_width_range=(10, 20),\n        ...     fill=\"inpaint_ns\",\n        ...     p=1.0\n        ... )\n        >>> transformed = aug_random(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n\n    References:\n        - CutOut: https://arxiv.org/abs/1708.04552\n        - Random Erasing: https://arxiv.org/abs/1708.04896\n        - OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        min_holes: int | None = Field(ge=0)\n        max_holes: int | None = Field(ge=0)\n        num_holes_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(1, None)),\n            AfterValidator(nondecreasing),\n        ]\n\n        min_height: ScalarType | None = Field(ge=0)\n        max_height: ScalarType | None = Field(ge=0)\n        hole_height_range: Annotated[\n            tuple[ScalarType, ScalarType],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(1, None)),\n        ]\n\n        min_width: ScalarType | None = Field(ge=0)\n        max_width: ScalarType | None = Field(ge=0)\n        hole_width_range: Annotated[\n            tuple[ScalarType, ScalarType],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(1, None)),\n        ]\n\n        @staticmethod\n        def update_range(\n            min_value: Number | None,\n            max_value: Number | None,\n            default_range: tuple[Number, Number],\n        ) -> tuple[Number, Number]:\n            return (min_value or max_value, max_value) if max_value is not None else default_range\n\n        @staticmethod\n        def validate_range(range_value: tuple[float, float], range_name: str, minimum: float = 0) -> None:\n            if not minimum <= range_value[0] <= range_value[1]:\n                raise ValueError(\n                    f\"First value in {range_name} should be less or equal than the second value \"\n                    f\"and at least {minimum}. Got: {range_value}\",\n                )\n            if isinstance(range_value[0], float) and not all(0 <= x <= 1 for x in range_value):\n                raise ValueError(f\"All values in {range_name} should be in [0, 1] range. Got: {range_value}\")\n\n        @model_validator(mode=\"after\")\n        def check_num_holes_and_dimensions(self) -> Self:\n            if self.min_holes is not None:\n                warn(\"`min_holes` is deprecated. Use num_holes_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_holes is not None:\n                warn(\"`max_holes` is deprecated. Use num_holes_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.min_height is not None:\n                warn(\"`min_height` is deprecated. Use hole_height_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_height is not None:\n                warn(\"`max_height` is deprecated. Use hole_height_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.min_width is not None:\n                warn(\"`min_width` is deprecated. Use hole_width_range instead.\", DeprecationWarning, stacklevel=2)\n            if self.max_width is not None:\n                warn(\"`max_width` is deprecated. Use hole_width_range instead.\", DeprecationWarning, stacklevel=2)\n\n            if self.max_holes is not None:\n                self.num_holes_range = self.update_range(self.min_holes, self.max_holes, self.num_holes_range)\n\n            self.validate_range(self.num_holes_range, \"num_holes_range\", minimum=1)\n\n            if self.max_height is not None:\n                self.hole_height_range = self.update_range(self.min_height, self.max_height, self.hole_height_range)\n            self.validate_range(self.hole_height_range, \"hole_height_range\")\n\n            if self.max_width is not None:\n                self.hole_width_range = self.update_range(self.min_width, self.max_width, self.hole_width_range)\n            self.validate_range(self.hole_width_range, \"hole_width_range\")\n\n            return self\n\n    def __init__(\n        self,\n        max_holes: int | None = None,\n        max_height: ScalarType | None = None,\n        max_width: ScalarType | None = None,\n        min_holes: int | None = None,\n        min_height: ScalarType | None = None,\n        min_width: ScalarType | None = None,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        num_holes_range: tuple[int, int] = (1, 1),\n        hole_height_range: tuple[ScalarType, ScalarType] = (8, 8),\n        hole_width_range: tuple[ScalarType, ScalarType] = (8, 8),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.num_holes_range = num_holes_range\n        self.hole_height_range = hole_height_range\n        self.hole_width_range = hole_width_range\n\n    def calculate_hole_dimensions(\n        self,\n        image_shape: tuple[int, int],\n        height_range: tuple[float, float],\n        width_range: tuple[float, float],\n        size: int,\n    ) -> tuple[np.ndarray, np.ndarray]:\n        \"\"\"Calculate random hole dimensions based on the provided ranges.\"\"\"\n        height, width = image_shape[:2]\n\n        if isinstance(height_range[0], int):\n            min_height = height_range[0]\n            max_height = min(height_range[1], height)\n\n            min_width = width_range[0]\n            max_width = min(width_range[1], width)\n\n            hole_heights = self.random_generator.integers(int(min_height), int(max_height + 1), size=size)\n            hole_widths = self.random_generator.integers(int(min_width), int(max_width + 1), size=size)\n\n        else:  # Assume float\n            hole_heights = (height * self.random_generator.uniform(*height_range, size=size)).astype(int)\n            hole_widths = (width * self.random_generator.uniform(*width_range, size=size)).astype(int)\n\n        return hole_heights, hole_widths\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        num_holes = self.py_random.randint(*self.num_holes_range)\n\n        hole_heights, hole_widths = self.calculate_hole_dimensions(\n            image_shape,\n            self.hole_height_range,\n            self.hole_width_range,\n            size=num_holes,\n        )\n\n        height, width = image_shape[:2]\n\n        y_min = self.random_generator.integers(0, height - hole_heights + 1, size=num_holes)\n        x_min = self.random_generator.integers(0, width - hole_widths + 1, size=num_holes)\n        y_max = y_min + hole_heights\n        x_max = x_min + hole_widths\n\n        holes = np.stack([x_min, y_min, x_max, y_max], axis=-1)\n\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (*super().get_transform_init_args_names(), \"num_holes_range\", \"hole_height_range\", \"hole_width_range\")\n
"},{"location":"api_reference/augmentations/dropout/coarse_dropout/#albumentations.augmentations.dropout.coarse_dropout.Erasing","title":"class Erasing (scale=(0.02, 0.33), ratio=(0.3, 3.3), fill=0, fill_mask=None, always_apply=None, p=0.5) [view source on GitHub]","text":"

Randomly erases rectangular regions in an image, following the Random Erasing Data Augmentation technique.

This augmentation helps improve model robustness by randomly masking out rectangular regions in the image, simulating occlusions and encouraging the model to learn from partial information. It's particularly effective for image classification and person re-identification tasks.

Parameters:

Name Type Description scale tuple[float, float]

Range for the proportion of image area to erase. The actual area will be randomly sampled from (scale[0] * image_area, scale[1] * image_area). Default: (0.02, 0.33)

ratio tuple[float, float]

Range for the aspect ratio (width/height) of the erased region. The actual ratio will be randomly sampled from (ratio[0], ratio[1]). Default: (0.3, 3.3)

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value used to fill the erased regions. Can be: - int or float: fills all channels with this value - tuple: fills each channel with corresponding value - \"random\": fills each pixel with random values - \"random_uniform\": fills entire erased region with a single random color - \"inpaint_telea\": uses OpenCV Telea inpainting method - \"inpaint_ns\": uses OpenCV Navier-Stokes inpainting method Default: 0

mask_fill ColorType | None

Value used to fill erased regions in the mask. If None, mask regions are not modified. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform attempts to find valid erasing parameters up to 10 times. If unsuccessful, no erasing is performed.
  • The actual erased area and aspect ratio are randomly sampled within the specified ranges for each application.
  • When using inpainting methods, only grayscale or RGB images are supported.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> # Basic usage with default parameters\n>>> transform = A.Erasing()\n>>> transformed = transform(image=image)\n>>> # Custom configuration\n>>> transform = A.Erasing(\n...     scale=(0.1, 0.4),\n...     ratio=(0.5, 2.0),\n...     fill_value=\"random_uniform\",\n...     p=1.0\n... )\n>>> transformed = transform(image=image)\n

References

  • Paper: https://arxiv.org/abs/1708.04896
  • Implementation inspired by torchvision: https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.RandomErasing

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/coarse_dropout.py Python
class Erasing(BaseDropout):\n    \"\"\"Randomly erases rectangular regions in an image, following the Random Erasing Data Augmentation technique.\n\n    This augmentation helps improve model robustness by randomly masking out rectangular regions in the image,\n    simulating occlusions and encouraging the model to learn from partial information. It's particularly\n    effective for image classification and person re-identification tasks.\n\n    Args:\n        scale (tuple[float, float]): Range for the proportion of image area to erase.\n            The actual area will be randomly sampled from (scale[0] * image_area, scale[1] * image_area).\n            Default: (0.02, 0.33)\n        ratio (tuple[float, float]): Range for the aspect ratio (width/height) of the erased region.\n            The actual ratio will be randomly sampled from (ratio[0], ratio[1]).\n            Default: (0.3, 3.3)\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value used to fill the erased regions. Can be:\n            - int or float: fills all channels with this value\n            - tuple: fills each channel with corresponding value\n            - \"random\": fills each pixel with random values\n            - \"random_uniform\": fills entire erased region with a single random color\n            - \"inpaint_telea\": uses OpenCV Telea inpainting method\n            - \"inpaint_ns\": uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        mask_fill (ColorType | None): Value used to fill erased regions in the mask.\n            If None, mask regions are not modified. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform attempts to find valid erasing parameters up to 10 times.\n          If unsuccessful, no erasing is performed.\n        - The actual erased area and aspect ratio are randomly sampled within\n          the specified ranges for each application.\n        - When using inpainting methods, only grayscale or RGB images are supported.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> # Basic usage with default parameters\n        >>> transform = A.Erasing()\n        >>> transformed = transform(image=image)\n        >>> # Custom configuration\n        >>> transform = A.Erasing(\n        ...     scale=(0.1, 0.4),\n        ...     ratio=(0.5, 2.0),\n        ...     fill_value=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> transformed = transform(image=image)\n\n    References:\n        - Paper: https://arxiv.org/abs/1708.04896\n        - Implementation inspired by torchvision:\n          https://pytorch.org/vision/stable/transforms.html#torchvision.transforms.RandomErasing\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        scale: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n        ratio: Annotated[\n            tuple[float, float],\n            AfterValidator(nondecreasing),\n            AfterValidator(check_range_bounds(0, None)),\n        ]\n\n    def __init__(\n        self,\n        scale: tuple[float, float] = (0.02, 0.33),\n        ratio: tuple[float, float] = (0.3, 3.3),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        always_apply: bool | None = None,\n        p: float = 0.5,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n\n        self.scale = scale\n        self.ratio = ratio\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Calculate erasing parameters using direct mathematical derivation.\n\n        Given:\n        - Image dimensions (H, W)\n        - Target area (A)\n        - Aspect ratio (r = w/h)\n\n        We know:\n        - h * w = A (area equation)\n        - w = r * h (aspect ratio equation)\n\n        Therefore:\n        - h * (r * h) = A\n        - h\u00b2 = A/r\n        - h = sqrt(A/r)\n        - w = r * sqrt(A/r) = sqrt(A*r)\n        \"\"\"\n        height, width = params[\"shape\"][:2]\n        total_area = height * width\n\n        # Calculate maximum valid area based on dimensions and aspect ratio\n        max_area = total_area * self.scale[1]\n        min_area = total_area * self.scale[0]\n\n        # For each aspect ratio r, the maximum area is constrained by:\n        # h = sqrt(A/r) \u2264 H and w = sqrt(A*r) \u2264 W\n        # Therefore: A \u2264 min(r*H\u00b2, W\u00b2/r)\n        r_min, r_max = self.ratio\n\n        def area_constraint_h(r: float) -> float:\n            return r * height * height\n\n        def area_constraint_w(r: float) -> float:\n            return width * width / r\n\n        # Find maximum valid area considering aspect ratio constraints\n        max_area_h = min(area_constraint_h(r_min), area_constraint_h(r_max))\n        max_area_w = min(area_constraint_w(r_min), area_constraint_w(r_max))\n        max_valid_area = min(max_area, max_area_h, max_area_w)\n\n        if max_valid_area < min_area:\n            return {\"holes\": np.array([], dtype=np.int32).reshape((0, 4))}\n\n        # Sample valid area and aspect ratio\n        erase_area = self.py_random.uniform(min_area, max_valid_area)\n\n        # Calculate valid aspect ratio range for this area\n        max_r = min(r_max, width * width / erase_area)\n        min_r = max(r_min, erase_area / (height * height))\n\n        if min_r > max_r:\n            return {\"holes\": np.array([], dtype=np.int32).reshape((0, 4))}\n\n        aspect_ratio = self.py_random.uniform(min_r, max_r)\n\n        # Calculate dimensions\n        h = int(round(np.sqrt(erase_area / aspect_ratio)))\n        w = int(round(np.sqrt(erase_area * aspect_ratio)))\n\n        # Sample position\n        top = self.py_random.randint(0, height - h)\n        left = self.py_random.randint(0, width - w)\n\n        holes = np.array([[left, top, left + w, top + h]], dtype=np.int32)\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"scale\", \"ratio\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/dropout/functional/","title":"Geometric functional transforms (augmentations.dropout.functional)","text":""},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.apply_inpainting","title":"def apply_inpainting (img, holes, method) [view source on GitHub]","text":"

Apply OpenCV inpainting to fill the holes in the image.

Parameters:

Name Type Description img np.ndarray

Input image (grayscale or BGR)

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

method InpaintMethod

Inpainting method to use (\"inpaint_telea\" or \"inpaint_ns\")

Returns:

Type Description np.ndarray

Inpainted image

Exceptions:

Type Description NotImplementedError

If image has more than 3 channels

Source code in albumentations/augmentations/dropout/functional.py Python
@uint8_io\ndef apply_inpainting(img: np.ndarray, holes: np.ndarray, method: InpaintMethod) -> np.ndarray:\n    \"\"\"Apply OpenCV inpainting to fill the holes in the image.\n\n    Args:\n        img: Input image (grayscale or BGR)\n        holes: Array of [x1, y1, x2, y2] coordinates\n        method: Inpainting method to use (\"inpaint_telea\" or \"inpaint_ns\")\n\n    Returns:\n        np.ndarray: Inpainted image\n\n    Raises:\n        NotImplementedError: If image has more than 3 channels\n    \"\"\"\n    num_channels = get_num_channels(img)\n    # Create inpainting mask\n    mask = np.zeros(img.shape[:2], dtype=np.uint8)\n    for x_min, y_min, x_max, y_max in holes:\n        mask[y_min:y_max, x_min:x_max] = 255\n\n    inpaint_method = cv2.INPAINT_TELEA if method == \"inpaint_telea\" else cv2.INPAINT_NS\n\n    # Handle grayscale images by converting to 3 channels and back\n    if num_channels == 1:\n        if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n            img = img.squeeze()\n        img_3ch = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)\n        result = cv2.inpaint(img_3ch, mask, 3, inpaint_method)\n        return (\n            cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)[..., None]\n            if num_channels == NUM_MULTI_CHANNEL_DIMENSIONS\n            else cv2.cvtColor(result, cv2.COLOR_BGR2GRAY)\n        )\n\n    return cv2.inpaint(img, mask, 3, inpaint_method)\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.calculate_grid_dimensions","title":"def calculate_grid_dimensions (image_shape, unit_size_range, holes_number_xy, random_generator) [view source on GitHub]","text":"

Calculate the dimensions of grid units for GridDropout.

This function determines the size of grid units based on the input parameters. It supports three modes of operation: 1. Using a range of unit sizes 2. Using a specified number of holes in x and y directions 3. Falling back to a default calculation

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

unit_size_range tuple[int, int] | None

A range of possible unit sizes. If provided, a random size within this range will be chosen for both height and width.

holes_number_xy tuple[int, int] | None

The number of holes in the x and y directions. If provided, the grid dimensions will be calculated to fit this number of holes.

random_generator np.random.Generator

The random generator to use for generating random values.

Returns:

Type Description tuple[int, int]

The calculated grid unit dimensions as (unit_height, unit_width).

Exceptions:

Type Description ValueError

If the upper limit of unit_size_range is greater than the shortest image edge.

Notes

  • If both unit_size_range and holes_number_xy are None, the function falls back to a default calculation, where the grid unit size is set to max(2, image_dimension // 10) for both height and width.
  • The function prioritizes unit_size_range over holes_number_xy if both are provided.
  • When using holes_number_xy, the actual number of holes may be slightly different due to integer division.

Examples:

Python
>>> image_shape = (100, 200)\n>>> calculate_grid_dimensions(image_shape, unit_size_range=(10, 20))\n(15, 15)  # Random value between 10 and 20\n
Python
>>> calculate_grid_dimensions(image_shape, holes_number_xy=(5, 10))\n(20, 20)  # 100 // 5 and 200 // 10\n
Python
>>> calculate_grid_dimensions(image_shape)\n(10, 20)  # Default calculation: max(2, dimension // 10)\n
Source code in albumentations/augmentations/dropout/functional.py Python
def calculate_grid_dimensions(\n    image_shape: tuple[int, int],\n    unit_size_range: tuple[int, int] | None,\n    holes_number_xy: tuple[int, int] | None,\n    random_generator: np.random.Generator,\n) -> tuple[int, int]:\n    \"\"\"Calculate the dimensions of grid units for GridDropout.\n\n    This function determines the size of grid units based on the input parameters.\n    It supports three modes of operation:\n    1. Using a range of unit sizes\n    2. Using a specified number of holes in x and y directions\n    3. Falling back to a default calculation\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        unit_size_range (tuple[int, int] | None, optional): A range of possible unit sizes.\n            If provided, a random size within this range will be chosen for both height and width.\n        holes_number_xy (tuple[int, int] | None, optional): The number of holes in the x and y directions.\n            If provided, the grid dimensions will be calculated to fit this number of holes.\n        random_generator (np.random.Generator): The random generator to use for generating random values.\n\n    Returns:\n        tuple[int, int]: The calculated grid unit dimensions as (unit_height, unit_width).\n\n    Raises:\n        ValueError: If the upper limit of unit_size_range is greater than the shortest image edge.\n\n    Notes:\n        - If both unit_size_range and holes_number_xy are None, the function falls back to a default calculation,\n          where the grid unit size is set to max(2, image_dimension // 10) for both height and width.\n        - The function prioritizes unit_size_range over holes_number_xy if both are provided.\n        - When using holes_number_xy, the actual number of holes may be slightly different due to integer division.\n\n    Examples:\n        >>> image_shape = (100, 200)\n        >>> calculate_grid_dimensions(image_shape, unit_size_range=(10, 20))\n        (15, 15)  # Random value between 10 and 20\n\n        >>> calculate_grid_dimensions(image_shape, holes_number_xy=(5, 10))\n        (20, 20)  # 100 // 5 and 200 // 10\n\n        >>> calculate_grid_dimensions(image_shape)\n        (10, 20)  # Default calculation: max(2, dimension // 10)\n    \"\"\"\n    height, width = image_shape[:2]\n\n    if unit_size_range is not None:\n        if unit_size_range[1] > min(image_shape[:2]):\n            raise ValueError(\"Grid size limits must be within the shortest image edge.\")\n        unit_size = random_generator.integers(*unit_size_range)\n        return unit_size, unit_size\n\n    if holes_number_xy:\n        holes_number_x, holes_number_y = holes_number_xy\n        unit_width = width // holes_number_x\n        unit_height = height // holes_number_y\n        return unit_height, unit_width\n\n    # Default fallback\n    unit_width = max(2, width // 10)\n    unit_height = max(2, height // 10)\n    return unit_height, unit_width\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.cutout","title":"def cutout (img, holes, fill_value, random_generator) [view source on GitHub]","text":"

Apply cutout augmentation to the image by cutting out holes and filling them.

Parameters:

Name Type Description img np.ndarray

The image to augment

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

fill_value DropoutFillValue

Value to fill holes with. Can be: - number (int/float): Will be broadcast to all channels - sequence (tuple/list/ndarray): Must match number of channels - \"random\": Different random values for each pixel - \"random_uniform\": Same random value for entire hole - \"inpaint_telea\"/\"inpaint_ns\": OpenCV inpainting methods

random_generator np.random.Generator

Random number generator for random fills

Exceptions:

Type Description ValueError

If fill_value length doesn't match number of channels

Source code in albumentations/augmentations/dropout/functional.py Python
def cutout(\n    img: np.ndarray,\n    holes: np.ndarray,\n    fill_value: DropoutFillValue,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Apply cutout augmentation to the image by cutting out holes and filling them.\n\n    Args:\n        img: The image to augment\n        holes: Array of [x1, y1, x2, y2] coordinates\n        fill_value: Value to fill holes with. Can be:\n            - number (int/float): Will be broadcast to all channels\n            - sequence (tuple/list/ndarray): Must match number of channels\n            - \"random\": Different random values for each pixel\n            - \"random_uniform\": Same random value for entire hole\n            - \"inpaint_telea\"/\"inpaint_ns\": OpenCV inpainting methods\n        random_generator: Random number generator for random fills\n\n    Raises:\n        ValueError: If fill_value length doesn't match number of channels\n    \"\"\"\n    img = img.copy()\n\n    # Handle inpainting methods\n    if isinstance(fill_value, str):\n        if fill_value in {\"inpaint_telea\", \"inpaint_ns\"}:\n            return apply_inpainting(img, holes, cast(InpaintMethod, fill_value))\n        if fill_value == \"random\":\n            return fill_holes_with_random(img, holes, random_generator, uniform=False)\n        if fill_value == \"random_uniform\":\n            return fill_holes_with_random(img, holes, random_generator, uniform=True)\n        raise ValueError(f\"Unsupported string fill_value: {fill_value}\")\n\n    # Convert numeric fill values to numpy array\n    if isinstance(fill_value, (int, float)):\n        fill_array = np.array(fill_value, dtype=img.dtype)\n        return fill_holes_with_value(img, holes, fill_array)\n\n    # Handle sequence fill values\n    fill_array = np.array(fill_value, dtype=img.dtype)\n\n    # For multi-channel images, verify fill_value matches number of channels\n    if img.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n        fill_array = fill_array.ravel()\n        if fill_array.size != img.shape[2]:\n            raise ValueError(\n                f\"Fill value must have same number of channels as image. \"\n                f\"Got {fill_array.size}, expected {img.shape[2]}\",\n            )\n\n    return fill_holes_with_value(img, holes, fill_array)\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.fill_holes_with_random","title":"def fill_holes_with_random (img, holes, random_generator, uniform) [view source on GitHub]","text":"

Fill holes with random values.

Parameters:

Name Type Description img np.ndarray

Input image

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

random_generator np.random.Generator

Random number generator

uniform bool

If True, use same random value for entire hole

Source code in albumentations/augmentations/dropout/functional.py Python
def fill_holes_with_random(\n    img: np.ndarray,\n    holes: np.ndarray,\n    random_generator: np.random.Generator,\n    uniform: bool,\n) -> np.ndarray:\n    \"\"\"Fill holes with random values.\n\n    Args:\n        img: Input image\n        holes: Array of [x1, y1, x2, y2] coordinates\n        random_generator: Random number generator\n        uniform: If True, use same random value for entire hole\n    \"\"\"\n    for x_min, y_min, x_max, y_max in holes:\n        shape = (1,) if uniform else (y_max - y_min, x_max - x_min)\n        if img.ndim != MONO_CHANNEL_DIMENSIONS:\n            shape = (1, img.shape[2]) if uniform else (*shape, img.shape[2])\n\n        random_fill = generate_random_fill(img.dtype, shape, random_generator)\n        img[y_min:y_max, x_min:x_max] = random_fill\n    return img\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.fill_holes_with_value","title":"def fill_holes_with_value (img, holes, fill_value) [view source on GitHub]","text":"

Fill holes with a constant value.

Parameters:

Name Type Description img np.ndarray

Input image

holes np.ndarray

Array of [x1, y1, x2, y2] coordinates

fill_value np.ndarray

Value to fill the holes with

Source code in albumentations/augmentations/dropout/functional.py Python
def fill_holes_with_value(img: np.ndarray, holes: np.ndarray, fill_value: np.ndarray) -> np.ndarray:\n    \"\"\"Fill holes with a constant value.\n\n    Args:\n        img: Input image\n        holes: Array of [x1, y1, x2, y2] coordinates\n        fill_value: Value to fill the holes with\n    \"\"\"\n    for x_min, y_min, x_max, y_max in holes:\n        img[y_min:y_max, x_min:x_max] = fill_value\n    return img\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.filter_bboxes_by_holes","title":"def filter_bboxes_by_holes (bboxes, holes, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Filter bounding boxes based on their remaining visible area and visibility ratio after intersection with holes.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes, each represented as [x_min, y_min, x_max, y_max].

holes np.ndarray

Array of holes, each represented as [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image (height, width).

min_area int

Minimum remaining visible area to keep the bounding box.

min_visibility float

Minimum visibility ratio to keep the bounding box. Calculated as 1 - (intersection_area / bbox_area).

Returns:

Type Description np.ndarray

Filtered array of bounding boxes.

Source code in albumentations/augmentations/dropout/functional.py Python
def filter_bboxes_by_holes(\n    bboxes: np.ndarray,\n    holes: np.ndarray,\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Filter bounding boxes based on their remaining visible area and visibility ratio after intersection with holes.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes, each represented as [x_min, y_min, x_max, y_max].\n        holes (np.ndarray): Array of holes, each represented as [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        min_area (int): Minimum remaining visible area to keep the bounding box.\n        min_visibility (float): Minimum visibility ratio to keep the bounding box.\n            Calculated as 1 - (intersection_area / bbox_area).\n\n    Returns:\n        np.ndarray: Filtered array of bounding boxes.\n    \"\"\"\n    if len(bboxes) == 0 or len(holes) == 0:\n        return bboxes\n\n    # Create a blank mask for holes\n    hole_mask = np.zeros(image_shape, dtype=np.uint8)\n\n    # Fill in the holes on the mask\n    for hole in holes:\n        x_min, y_min, x_max, y_max = hole.astype(int)\n        hole_mask[y_min:y_max, x_min:x_max] = 1\n\n    # Vectorized calculation\n    bboxes_int = bboxes.astype(int)\n    x_min, y_min, x_max, y_max = bboxes_int[:, 0], bboxes_int[:, 1], bboxes_int[:, 2], bboxes_int[:, 3]\n\n    # Calculate box areas\n    box_areas = (x_max - x_min) * (y_max - y_min)\n\n    # Create a mask of the same shape as bboxes\n    mask = np.zeros(len(bboxes), dtype=bool)\n\n    for i in range(len(bboxes)):\n        intersection_area = np.sum(hole_mask[y_min[i] : y_max[i], x_min[i] : x_max[i]])\n        remaining_area = box_areas[i] - intersection_area\n        visibility_ratio = 1 - (intersection_area / box_areas[i])\n        mask[i] = (remaining_area >= min_area) and (visibility_ratio >= min_visibility)\n\n    return bboxes[mask]\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.filter_keypoints_in_holes","title":"def filter_keypoints_in_holes (keypoints, holes) [view source on GitHub]","text":"

Filter out keypoints that are inside any of the holes.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (num_keypoints, 2+). The first two columns are x and y coordinates.

holes np.ndarray

Array of holes with shape (num_holes, 4). Each hole is represented as [x1, y1, x2, y2].

Returns:

Type Description np.ndarray

Array of keypoints that are not inside any hole.

Source code in albumentations/augmentations/dropout/functional.py Python
@handle_empty_array(\"keypoints\")\ndef filter_keypoints_in_holes(keypoints: np.ndarray, holes: np.ndarray) -> np.ndarray:\n    \"\"\"Filter out keypoints that are inside any of the holes.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (num_keypoints, 2+).\n                                The first two columns are x and y coordinates.\n        holes (np.ndarray): Array of holes with shape (num_holes, 4).\n                            Each hole is represented as [x1, y1, x2, y2].\n\n    Returns:\n        np.ndarray: Array of keypoints that are not inside any hole.\n    \"\"\"\n    # Broadcast keypoints and holes for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    hole_x1 = holes[:, 0]  # Shape: (num_holes,)\n    hole_y1 = holes[:, 1]  # Shape: (num_holes,)\n    hole_x2 = holes[:, 2]  # Shape: (num_holes,)\n    hole_y2 = holes[:, 3]  # Shape: (num_holes,)\n\n    # Check if each keypoint is inside each hole\n    inside_hole = (kp_x >= hole_x1) & (kp_x < hole_x2) & (kp_y >= hole_y1) & (kp_y < hole_y2)\n\n    # A keypoint is valid if it's not inside any hole\n    valid_keypoints = ~np.any(inside_hole, axis=1)\n\n    return keypoints[valid_keypoints]\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.generate_grid_holes","title":"def generate_grid_holes (image_shape, grid, ratio, random_offset, shift_xy, random_generator) [view source on GitHub]","text":"

Generate a list of holes for GridDropout using a uniform grid.

This function creates a grid of holes for use in the GridDropout augmentation technique. It allows for customization of the grid size, hole size ratio, and positioning of holes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns). This determines the number of cells in the grid, where each cell may contain a hole.

ratio float

The ratio of the hole size to the grid cell size. Should be between 0 and 1. A ratio of 1 means the hole will fill the entire grid cell.

random_offset bool

If True, applies random offsets to each hole within its grid cell. If False, uses the global shift specified by shift_xy.

shift_xy tuple[int, int]

The global shift to apply to all holes as (shift_x, shift_y). Only used when random_offset is False.

random_generator np.random.Generator

The random generator for generating random offsets and shuffling. If None, a new Generator will be created.

Returns:

Type Description np.ndarray

An array of hole coordinates, where each hole is represented as [x1, y1, x2, y2]. The shape of the array is (n_holes, 4), where n_holes is determined by the grid size.

Notes

  • The function first creates a uniform grid based on the image shape and specified grid size.
  • Hole sizes are calculated based on the provided ratio and grid cell sizes.
  • If random_offset is True, each hole is randomly positioned within its grid cell.
  • If random_offset is False, all holes are shifted by the global shift_xy value.
  • The function ensures that all holes remain within the image boundaries.

Examples:

Python
>>> image_shape = (100, 100)\n>>> grid = (5, 5)\n>>> ratio = 0.5\n>>> random_offset = True\n>>> random_state = np.random.RandomState(42)\n>>> shift_xy = (0, 0)\n>>> holes = generate_grid_holes(image_shape, grid, ratio, random_offset, random_state, shift_xy)\n>>> print(holes.shape)\n(25, 4)\n>>> print(holes[0])  # Example output: [x1, y1, x2, y2] of the first hole\n[ 1 21 11 31]\n
Source code in albumentations/augmentations/dropout/functional.py Python
def generate_grid_holes(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    ratio: float,\n    random_offset: bool,\n    shift_xy: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a list of holes for GridDropout using a uniform grid.\n\n    This function creates a grid of holes for use in the GridDropout augmentation technique.\n    It allows for customization of the grid size, hole size ratio, and positioning of holes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns). This determines the number of cells\n            in the grid, where each cell may contain a hole.\n        ratio (float): The ratio of the hole size to the grid cell size. Should be between 0 and 1.\n            A ratio of 1 means the hole will fill the entire grid cell.\n        random_offset (bool): If True, applies random offsets to each hole within its grid cell.\n            If False, uses the global shift specified by shift_xy.\n        shift_xy (tuple[int, int]): The global shift to apply to all holes as (shift_x, shift_y).\n            Only used when random_offset is False.\n        random_generator (np.random.Generator): The random generator for generating random offsets\n            and shuffling. If None, a new Generator will be created.\n\n    Returns:\n        np.ndarray: An array of hole coordinates, where each hole is represented as\n            [x1, y1, x2, y2]. The shape of the array is (n_holes, 4), where n_holes\n            is determined by the grid size.\n\n    Notes:\n        - The function first creates a uniform grid based on the image shape and specified grid size.\n        - Hole sizes are calculated based on the provided ratio and grid cell sizes.\n        - If random_offset is True, each hole is randomly positioned within its grid cell.\n        - If random_offset is False, all holes are shifted by the global shift_xy value.\n        - The function ensures that all holes remain within the image boundaries.\n\n    Examples:\n        >>> image_shape = (100, 100)\n        >>> grid = (5, 5)\n        >>> ratio = 0.5\n        >>> random_offset = True\n        >>> random_state = np.random.RandomState(42)\n        >>> shift_xy = (0, 0)\n        >>> holes = generate_grid_holes(image_shape, grid, ratio, random_offset, random_state, shift_xy)\n        >>> print(holes.shape)\n        (25, 4)\n        >>> print(holes[0])  # Example output: [x1, y1, x2, y2] of the first hole\n        [ 1 21 11 31]\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Generate the uniform grid\n    cells = split_uniform_grid(image_shape, grid, random_generator)\n\n    # Calculate hole sizes based on the ratio\n    cell_heights = cells[:, 2] - cells[:, 0]\n    cell_widths = cells[:, 3] - cells[:, 1]\n    hole_heights = np.clip(cell_heights * ratio, 1, cell_heights - 1).astype(int)\n    hole_widths = np.clip(cell_widths * ratio, 1, cell_widths - 1).astype(int)\n\n    # Calculate maximum possible offsets\n    max_offset_y = cell_heights - hole_heights\n    max_offset_x = cell_widths - hole_widths\n\n    if random_offset:\n        # Generate random offsets for each hole\n        offset_y = random_generator.integers(0, max_offset_y + 1)\n        offset_x = random_generator.integers(0, max_offset_x + 1)\n    else:\n        # Use global shift\n        offset_y = np.full_like(max_offset_y, shift_xy[1])\n        offset_x = np.full_like(max_offset_x, shift_xy[0])\n\n    # Calculate hole coordinates\n    x_min = np.clip(cells[:, 1] + offset_x, 0, width - hole_widths)\n    y_min = np.clip(cells[:, 0] + offset_y, 0, height - hole_heights)\n    x_max = np.minimum(x_min + hole_widths, width)\n    y_max = np.minimum(y_min + hole_heights, height)\n\n    return np.column_stack((x_min, y_min, x_max, y_max))\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.generate_random_fill","title":"def generate_random_fill (dtype, shape, random_generator) [view source on GitHub]","text":"

Generate a random fill array based on the given dtype and target shape.

This function creates a numpy array filled with random values. The range and type of these values depend on the input dtype. For integer dtypes, it generates random integers. For floating-point dtypes, it generates random floats.

Parameters:

Name Type Description dtype np.dtype

The data type of the array to be generated.

shape tuple[int, ...]

The shape of the array to be generated.

random_generator np.random.Generator

The random generator to use for generating values. If None, the default numpy random generator is used.

Returns:

Type Description np.ndarray

A numpy array of the specified shape and dtype, filled with random values.

Exceptions:

Type Description ValueError

If the input dtype is neither integer nor floating-point.

Examples:

Python
>>> import numpy as np\n>>> random_state = np.random.RandomState(42)\n>>> result = generate_random_fill(np.dtype('uint8'), (2, 2), random_state)\n>>> print(result)\n[[172 251]\n [ 80 141]]\n
Source code in albumentations/augmentations/dropout/functional.py Python
def generate_random_fill(\n    dtype: np.dtype,\n    shape: tuple[int, ...],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate a random fill array based on the given dtype and target shape.\n\n    This function creates a numpy array filled with random values. The range and type of these values\n    depend on the input dtype. For integer dtypes, it generates random integers. For floating-point\n    dtypes, it generates random floats.\n\n    Args:\n        dtype (np.dtype): The data type of the array to be generated.\n        shape (tuple[int, ...]): The shape of the array to be generated.\n        random_generator (np.random.Generator): The random generator to use for generating values.\n            If None, the default numpy random generator is used.\n\n    Returns:\n        np.ndarray: A numpy array of the specified shape and dtype, filled with random values.\n\n    Raises:\n        ValueError: If the input dtype is neither integer nor floating-point.\n\n    Examples:\n        >>> import numpy as np\n        >>> random_state = np.random.RandomState(42)\n        >>> result = generate_random_fill(np.dtype('uint8'), (2, 2), random_state)\n        >>> print(result)\n        [[172 251]\n         [ 80 141]]\n    \"\"\"\n    max_value = MAX_VALUES_BY_DTYPE[dtype]\n    if np.issubdtype(dtype, np.integer):\n        return random_generator.integers(0, max_value + 1, size=shape, dtype=dtype)\n    if np.issubdtype(dtype, np.floating):\n        return random_generator.uniform(0, max_value, size=shape).astype(dtype)\n    raise ValueError(f\"Unsupported dtype: {dtype}\")\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.label","title":"def label (mask, return_num=False, connectivity=2) [view source on GitHub]","text":"

Label connected regions of an integer array.

This function uses OpenCV's connectedComponents under the hood but mimics the behavior of scikit-image's label function.

Parameters:

Name Type Description mask np.ndarray

The array to label. Must be of integer type.

return_num bool

If True, return the number of labels (default: False).

connectivity int

Maximum number of orthogonal hops to consider a pixel/voxel as a neighbor. Accepted values are 1 or 2. Default is 2.

Returns:

Type Description np.ndarray | tuple[np.ndarray, int]

Labeled array, where all connected regions are assigned the same integer value. If return_num is True, it also returns the number of labels.

Source code in albumentations/augmentations/dropout/functional.py Python
def label(mask: np.ndarray, return_num: bool = False, connectivity: int = 2) -> np.ndarray | tuple[np.ndarray, int]:\n    \"\"\"Label connected regions of an integer array.\n\n    This function uses OpenCV's connectedComponents under the hood but mimics\n    the behavior of scikit-image's label function.\n\n    Args:\n        mask (np.ndarray): The array to label. Must be of integer type.\n        return_num (bool): If True, return the number of labels (default: False).\n        connectivity (int): Maximum number of orthogonal hops to consider a pixel/voxel\n                            as a neighbor. Accepted values are 1 or 2. Default is 2.\n\n    Returns:\n        np.ndarray | tuple[np.ndarray, int]: Labeled array, where all connected regions are\n        assigned the same integer value. If return_num is True, it also returns the number of labels.\n    \"\"\"\n    # Create a copy of the original mask\n    labeled = np.zeros_like(mask, dtype=np.int32)\n\n    # Get unique non-zero values from the original mask\n    unique_values = np.unique(mask[mask != 0])\n\n    # Label each unique value separately\n    next_label = 1\n    for value in unique_values:\n        binary_mask = (mask == value).astype(np.uint8)\n\n        # Set connectivity for OpenCV (4 or 8)\n        cv2_connectivity = 4 if connectivity == 1 else 8\n\n        # Use OpenCV's connectedComponents\n        num_labels, labels = cv2.connectedComponents(binary_mask, connectivity=cv2_connectivity)\n\n        # Assign new labels\n        for i in range(1, num_labels):\n            labeled[labels == i] = next_label\n            next_label += 1\n\n    num_labels = next_label - 1\n\n    return (labeled, num_labels) if return_num else labeled\n
"},{"location":"api_reference/augmentations/dropout/functional/#albumentations.augmentations.dropout.functional.mask_dropout_bboxes","title":"def mask_dropout_bboxes (bboxes, dropout_mask, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Filter out bounding boxes based on their intersection with the dropout mask.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) in format [x_min, y_min, x_max, y_max, ...].

dropout_mask np.ndarray

Boolean mask of shape (height, width) where True values indicate dropped out regions.

image_shape Tuple[int, int]

The shape of the original image as (height, width).

min_area float

Minimum area of the bounding box to be kept.

min_visibility float

Minimum visibility ratio of the bounding box to be kept.

Returns:

Type Description np.ndarray

Filtered array of bounding boxes.

Source code in albumentations/augmentations/dropout/functional.py Python
@handle_empty_array(\"bboxes\")\ndef mask_dropout_bboxes(\n    bboxes: np.ndarray,\n    dropout_mask: np.ndarray,\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Filter out bounding boxes based on their intersection with the dropout mask.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (N, 4+) in format [x_min, y_min, x_max, y_max, ...].\n        dropout_mask (np.ndarray): Boolean mask of shape (height, width) where True values indicate dropped out regions.\n        image_shape (Tuple[int, int]): The shape of the original image as (height, width).\n        min_area (float): Minimum area of the bounding box to be kept.\n        min_visibility (float): Minimum visibility ratio of the bounding box to be kept.\n\n    Returns:\n        np.ndarray: Filtered array of bounding boxes.\n    \"\"\"\n    height, width = image_shape\n\n    # Create binary masks for each bounding box\n    y, x = np.ogrid[:height, :width]\n    box_masks = (\n        (x[None, :] >= bboxes[:, 0, None, None])\n        & (x[None, :] <= bboxes[:, 2, None, None])\n        & (y[None, :] >= bboxes[:, 1, None, None])\n        & (y[None, :] <= bboxes[:, 3, None, None])\n    )\n\n    # Calculate the area of each bounding box\n    box_areas = (bboxes[:, 2] - bboxes[:, 0]) * (bboxes[:, 3] - bboxes[:, 1])\n\n    # Calculate the visible area of each box (non-intersecting area with dropout mask)\n    visible_areas = np.sum(box_masks & ~dropout_mask.squeeze(), axis=(1, 2))\n\n    # Calculate visibility ratio (visible area / total box area)\n    visibility_ratio = visible_areas / box_areas\n\n    # Create a boolean mask for boxes to keep\n    keep_mask = (visible_areas >= min_area) & (visibility_ratio >= min_visibility)\n\n    return bboxes[keep_mask]\n
"},{"location":"api_reference/augmentations/dropout/grid_dropout/","title":"GridDropout augmentation (augmentations.dropout.grid_dropout)","text":""},{"location":"api_reference/augmentations/dropout/grid_dropout/#albumentations.augmentations.dropout.grid_dropout.GridDropout","title":"class GridDropout (ratio=0.5, unit_size_min=None, unit_size_max=None, holes_number_x=None, holes_number_y=None, shift_x=None, shift_y=None, random_offset=True, fill_value=None, mask_fill_value=None, unit_size_range=None, holes_number_xy=None, shift_xy=(0, 0), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply GridDropout augmentation to images, masks, bounding boxes, and keypoints.

GridDropout drops out rectangular regions of an image and the corresponding mask in a grid fashion. This technique can help improve model robustness by forcing the network to rely on a broader context rather than specific local features.

Parameters:

Name Type Description ratio float

The ratio of the mask holes to the unit size (same for horizontal and vertical directions). Must be between 0 and 1. Default: 0.5.

unit_size_range tuple[int, int] | None

Range from which to sample grid size. Default: None. Must be between 2 and the image's shorter edge. If None, grid size is calculated based on image size.

holes_number_xy tuple[int, int] | None

The number of grid units in x and y directions. First value should be between 1 and image width//2, Second value should be between 1 and image height//2. Default: None. If provided, overrides unit_size_range.

random_offset bool

Whether to offset the grid randomly between 0 and (grid unit size - hole size). If True, entered shift_xy is ignored and set randomly. Default: True.

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

fill_mask ColorType | None

Value for the dropped pixels in mask. If None, the mask is not modified. Default: None.

shift_xy tuple[int, int]

Offsets of the grid start in x and y directions from (0,0) coordinate. Only used when random_offset is False. Default: (0, 0).

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If both unit_size_range and holes_number_xy are None, the grid size is calculated based on the image size.
  • The actual number of dropped regions may differ slightly from holes_number_xy due to rounding.
  • Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.
  • For 'random_uniform' fill, each grid cell gets a single random color, unlike 'random' where each pixel gets its own random value.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n>>> # Example with standard fill value\n>>> aug_basic = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     random_offset=True,\n...     p=1.0\n... )\n>>> # Example with random uniform fill\n>>> aug_random = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     fill=\"random_uniform\",\n...     p=1.0\n... )\n>>> # Example with inpainting\n>>> aug_inpaint = A.GridDropout(\n...     ratio=0.3,\n...     unit_size_range=(10, 20),\n...     fill=\"inpaint_ns\",\n...     p=1.0\n... )\n>>> transformed = aug_random(image=image, mask=mask)\n>>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n

Reference

  • Paper: https://arxiv.org/abs/2001.04086
  • OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/grid_dropout.py Python
class GridDropout(BaseDropout):\n    \"\"\"Apply GridDropout augmentation to images, masks, bounding boxes, and keypoints.\n\n    GridDropout drops out rectangular regions of an image and the corresponding mask in a grid fashion.\n    This technique can help improve model robustness by forcing the network to rely on a broader context\n    rather than specific local features.\n\n    Args:\n        ratio (float): The ratio of the mask holes to the unit size (same for horizontal and vertical directions).\n            Must be between 0 and 1. Default: 0.5.\n        unit_size_range (tuple[int, int] | None): Range from which to sample grid size. Default: None.\n            Must be between 2 and the image's shorter edge. If None, grid size is calculated based on image size.\n        holes_number_xy (tuple[int, int] | None): The number of grid units in x and y directions.\n            First value should be between 1 and image width//2,\n            Second value should be between 1 and image height//2.\n            Default: None. If provided, overrides unit_size_range.\n        random_offset (bool): Whether to offset the grid randomly between 0 and (grid unit size - hole size).\n            If True, entered shift_xy is ignored and set randomly. Default: True.\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        fill_mask (ColorType | None): Value for the dropped pixels in mask.\n            If None, the mask is not modified. Default: None.\n        shift_xy (tuple[int, int]): Offsets of the grid start in x and y directions from (0,0) coordinate.\n            Only used when random_offset is False. Default: (0, 0).\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If both unit_size_range and holes_number_xy are None, the grid size is calculated based on the image size.\n        - The actual number of dropped regions may differ slightly from holes_number_xy due to rounding.\n        - Inpainting methods ('inpaint_telea', 'inpaint_ns') work only with grayscale or RGB images.\n        - For 'random_uniform' fill, each grid cell gets a single random color, unlike 'random' where each pixel\n            gets its own random value.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)\n        >>> # Example with standard fill value\n        >>> aug_basic = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     random_offset=True,\n        ...     p=1.0\n        ... )\n        >>> # Example with random uniform fill\n        >>> aug_random = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     fill=\"random_uniform\",\n        ...     p=1.0\n        ... )\n        >>> # Example with inpainting\n        >>> aug_inpaint = A.GridDropout(\n        ...     ratio=0.3,\n        ...     unit_size_range=(10, 20),\n        ...     fill=\"inpaint_ns\",\n        ...     p=1.0\n        ... )\n        >>> transformed = aug_random(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = transformed[\"image\"], transformed[\"mask\"]\n\n    Reference:\n        - Paper: https://arxiv.org/abs/2001.04086\n        - OpenCV Inpainting methods: https://docs.opencv.org/master/df/d3d/tutorial_py_inpainting.html\n    \"\"\"\n\n    class InitSchema(BaseDropout.InitSchema):\n        ratio: float = Field(gt=0, le=1)\n\n        unit_size_min: int | None = Field(ge=2)\n        unit_size_max: int | None = Field(ge=2)\n\n        holes_number_x: int | None = Field(ge=1)\n        holes_number_y: int | None = Field(ge=1)\n\n        shift_x: int | None = Field(ge=0)\n        shift_y: int | None = Field(ge=0)\n\n        random_offset: bool\n        fill_value: DropoutFillValue | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_fill_value: ColorType | None = Field(deprecated=\"Deprecated use fill_mask instead\")\n\n        unit_size_range: (\n            Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None)), AfterValidator(nondecreasing)]\n            | None\n        )\n        shift_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(0, None))]\n\n        holes_number_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))] | None\n\n        @model_validator(mode=\"after\")\n        def validate_normalization(self) -> Self:\n            if self.unit_size_min is not None and self.unit_size_max is not None:\n                self.unit_size_range = self.unit_size_min, self.unit_size_max\n                warn(\n                    \"unit_size_min and unit_size_max are deprecated. Use unit_size_range instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.shift_x is not None and self.shift_y is not None:\n                self.shift_xy = self.shift_x, self.shift_y\n                warn(\"shift_x and shift_y are deprecated. Use shift_xy instead.\", DeprecationWarning, stacklevel=2)\n\n            if self.holes_number_x is not None and self.holes_number_y is not None:\n                self.holes_number_xy = self.holes_number_x, self.holes_number_y\n                warn(\n                    \"holes_number_x and holes_number_y are deprecated. Use holes_number_xy instead.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n\n            if self.unit_size_range and not MIN_UNIT_SIZE <= self.unit_size_range[0] <= self.unit_size_range[1]:\n                raise ValueError(\"Max unit size should be >= min size, both at least 2 pixels.\")\n\n            return self\n\n    def __init__(\n        self,\n        ratio: float = 0.5,\n        unit_size_min: int | None = None,\n        unit_size_max: int | None = None,\n        holes_number_x: int | None = None,\n        holes_number_y: int | None = None,\n        shift_x: int | None = None,\n        shift_y: int | None = None,\n        random_offset: bool = True,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        unit_size_range: tuple[int, int] | None = None,\n        holes_number_xy: tuple[int, int] | None = None,\n        shift_xy: tuple[int, int] = (0, 0),\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.ratio = ratio\n        self.unit_size_range = unit_size_range\n        self.holes_number_xy = holes_number_xy\n        self.random_offset = random_offset\n        self.shift_xy = shift_xy\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        image_shape = params[\"shape\"]\n        if self.holes_number_xy:\n            grid = self.holes_number_xy\n        else:\n            # Calculate grid based on unit_size_range or default\n            unit_height, unit_width = fdropout.calculate_grid_dimensions(\n                image_shape,\n                self.unit_size_range,\n                self.holes_number_xy,\n                self.random_generator,\n            )\n            grid = (image_shape[0] // unit_height, image_shape[1] // unit_width)\n\n        holes = fdropout.generate_grid_holes(\n            image_shape,\n            grid,\n            self.ratio,\n            self.random_offset,\n            self.shift_xy,\n            self.random_generator,\n        )\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"ratio\",\n            \"unit_size_range\",\n            \"holes_number_xy\",\n            \"shift_xy\",\n            \"random_offset\",\n        )\n
"},{"location":"api_reference/augmentations/dropout/mask_dropout/","title":"MaskDropout augmentation (augmentations.dropout.mask_dropout)","text":""},{"location":"api_reference/augmentations/dropout/mask_dropout/#albumentations.augmentations.dropout.mask_dropout.MaskDropout","title":"class MaskDropout (max_objects=(1, 1), image_fill_value=None, mask_fill_value=None, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply dropout to random objects in a mask, zeroing out the corresponding regions in both the image and mask.

This transform identifies objects in the mask (where each unique non-zero value represents a distinct object), randomly selects a number of these objects, and sets their corresponding regions to zero in both the image and mask. It can also handle bounding boxes and keypoints, removing or adjusting them based on the dropout regions.

Parameters:

Name Type Description max_objects int | tuple[int, int]

Maximum number of objects to dropout. If a single int is provided, it's treated as the upper bound. If a tuple of two ints is provided, it's treated as a range [min, max].

fill float | str | Literal[\"inpaint\"]

Value to fill the dropped out regions in the image. If set to 'inpaint', it applies inpainting to the dropped out regions (works only for 3-channel images).

fill_mask float | int

Value to fill the dropped out regions in the mask.

min_area float

Minimum area (in pixels) of a bounding box that must remain visible after dropout to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0

min_visibility float

Minimum visibility ratio (visible area / total area) of a bounding box after dropout to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The mask should be a single-channel image where 0 represents the background and non-zero values represent different object instances.
  • For bounding box and keypoint augmentation, make sure to set up the corresponding processors in the pipeline.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>>\n>>> # Define a sample image, mask, and bounding boxes\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> mask = np.zeros((100, 100), dtype=np.uint8)\n>>> mask[20:40, 20:40] = 1  # Object 1\n>>> mask[60:80, 60:80] = 2  # Object 2\n>>> bboxes = np.array([[20, 20, 40, 40], [60, 60, 80, 80]])\n>>>\n>>> # Define the transform\n>>> transform = A.Compose([\n...     A.MaskDropout(max_objects=1, mask_fill_value=0, min_area=100, min_visibility=0.5, p=1.0),\n... ], bbox_params=A.BboxParams(format='pascal_voc', min_area=1, min_visibility=0.1))\n>>>\n>>> # Apply the transform\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes)\n>>>\n>>> # The result will have one of the objects dropped out in both image and mask,\n>>> # and the corresponding bounding box removed if it doesn't meet the area and visibility criteria\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/mask_dropout.py Python
class MaskDropout(DualTransform):\n    \"\"\"Apply dropout to random objects in a mask, zeroing out the corresponding regions in both the image and mask.\n\n    This transform identifies objects in the mask (where each unique non-zero value represents a distinct object),\n    randomly selects a number of these objects, and sets their corresponding regions to zero in both the image and mask.\n    It can also handle bounding boxes and keypoints, removing or adjusting them based on the dropout regions.\n\n    Args:\n        max_objects (int | tuple[int, int]): Maximum number of objects to dropout. If a single int is provided,\n            it's treated as the upper bound. If a tuple of two ints is provided, it's treated as a range [min, max].\n        fill (float | str | Literal[\"inpaint\"]): Value to fill the dropped out regions in the image.\n            If set to 'inpaint', it applies inpainting to the dropped out regions (works only for 3-channel images).\n        fill_mask (float | int): Value to fill the dropped out regions in the mask.\n        min_area (float): Minimum area (in pixels) of a bounding box that must remain visible after dropout to be kept.\n            Only applicable if bounding box augmentation is enabled. Default: 0.0\n        min_visibility (float): Minimum visibility ratio (visible area / total area) of a bounding box after dropout\n            to be kept. Only applicable if bounding box augmentation is enabled. Default: 0.0\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The mask should be a single-channel image where 0 represents the background and non-zero values represent\n          different object instances.\n        - For bounding box and keypoint augmentation, make sure to set up the corresponding processors in the pipeline.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>>\n        >>> # Define a sample image, mask, and bounding boxes\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> mask = np.zeros((100, 100), dtype=np.uint8)\n        >>> mask[20:40, 20:40] = 1  # Object 1\n        >>> mask[60:80, 60:80] = 2  # Object 2\n        >>> bboxes = np.array([[20, 20, 40, 40], [60, 60, 80, 80]])\n        >>>\n        >>> # Define the transform\n        >>> transform = A.Compose([\n        ...     A.MaskDropout(max_objects=1, mask_fill_value=0, min_area=100, min_visibility=0.5, p=1.0),\n        ... ], bbox_params=A.BboxParams(format='pascal_voc', min_area=1, min_visibility=0.1))\n        >>>\n        >>> # Apply the transform\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes)\n        >>>\n        >>> # The result will have one of the objects dropped out in both image and mask,\n        >>> # and the corresponding bounding box removed if it doesn't meet the area and visibility criteria\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_objects: OnePlusIntRangeType\n\n        image_fill_value: float | Literal[\"inpaint\"] | None = Field(deprecated=\"Deprecated use fill instead\")\n        mask_fill_value: float | None = Field(deprecated=\"Deprecated use fill_mask instead\")\n\n        fill: float | Literal[\"inpaint\"]\n        fill_mask: float\n\n    def __init__(\n        self,\n        max_objects: ScaleIntType = (1, 1),\n        image_fill_value: float | Literal[\"inpaint\"] | None = None,\n        mask_fill_value: float | None = None,\n        fill: float | Literal[\"inpaint\"] = 0,\n        fill_mask: float = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_objects = cast(tuple[int, int], max_objects)\n        self.fill = fill  # type: ignore[assignment]\n        self.fill_mask = fill_mask\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [\"mask\"]\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        mask = data[\"mask\"]\n\n        label_image, num_labels = fdropout.label(mask, return_num=True)\n\n        if num_labels == 0:\n            dropout_mask = None\n        else:\n            objects_to_drop = self.py_random.randint(*self.max_objects)\n            objects_to_drop = min(num_labels, objects_to_drop)\n\n            if objects_to_drop == num_labels:\n                dropout_mask = mask > 0\n            else:\n                labels_index = self.py_random.sample(range(1, num_labels + 1), objects_to_drop)\n                dropout_mask = np.zeros(mask.shape[:2], dtype=bool)\n                for label_index in labels_index:\n                    dropout_mask |= label_image == label_index\n\n        return {\"dropout_mask\": dropout_mask}\n\n    def apply(self, img: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return img\n\n        if self.fill == \"inpaint\":\n            dropout_mask = dropout_mask.astype(np.uint8)\n            _, _, width, height = cv2.boundingRect(dropout_mask)\n            radius = min(3, max(width, height) // 2)\n            return cv2.inpaint(img, dropout_mask, radius, cv2.INPAINT_NS)\n\n        img = img.copy()\n        img[dropout_mask] = self.fill\n\n        return img\n\n    def apply_to_mask(self, mask: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return mask\n\n        mask = mask.copy()\n        mask[dropout_mask] = self.fill_mask\n        return mask\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return bboxes\n\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n\n        image_shape = params[\"shape\"][:2]\n\n        denormalized_bboxes = denormalize_bboxes(bboxes, image_shape)\n\n        result = fdropout.mask_dropout_bboxes(\n            denormalized_bboxes,\n            dropout_mask,\n            image_shape,\n            processor.params.min_area,\n            processor.params.min_visibility,\n        )\n\n        return normalize_bboxes(result, image_shape)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, dropout_mask: np.ndarray | None, **params: Any) -> np.ndarray:\n        if dropout_mask is None:\n            return keypoints\n\n        processor = cast(KeypointsProcessor, self.get_processor(\"keypoints\"))\n\n        if processor is None or not processor.params.remove_invisible:\n            return keypoints\n\n        return fdropout.mask_dropout_keypoints(keypoints, dropout_mask)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_objects\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/dropout/xy_masking/","title":"XYMasking augmentation (augmentations.dropout.xy_masking)","text":""},{"location":"api_reference/augmentations/dropout/xy_masking/#albumentations.augmentations.dropout.xy_masking.XYMasking","title":"class XYMasking (num_masks_x=0, num_masks_y=0, mask_x_length=0, mask_y_length=0, fill_value=None, mask_fill_value=None, fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

Applies masking strips to an image, either horizontally (X axis) or vertically (Y axis), simulating occlusions. This transform is useful for training models to recognize images with varied visibility conditions. It's particularly effective for spectrogram images, allowing spectral and frequency masking to improve model robustness.

At least one of max_x_length or max_y_length must be specified, dictating the mask's maximum size along each axis.

Parameters:

Name Type Description num_masks_x int | tuple[int, int]

Number or range of horizontal regions to mask. Defaults to 0.

num_masks_y int | tuple[int, int]

Number or range of vertical regions to mask. Defaults to 0.

mask_x_length int | tuple[int, int]

Specifies the length of the masks along the X (horizontal) axis. If an integer is provided, it sets a fixed mask length. If a tuple of two integers (min, max) is provided, the mask length is randomly chosen within this range for each mask. This allows for variable-length masks in the horizontal direction.

mask_y_length int | tuple[int, int]

Specifies the height of the masks along the Y (vertical) axis. Similar to mask_x_length, an integer sets a fixed mask height, while a tuple (min, max) allows for variable-height masks, chosen randomly within the specified range for each mask. This flexibility facilitates creating masks of various sizes in the vertical direction.

fill ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]

Value for the dropped pixels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel - 'random': each pixel is filled with random values - 'random_uniform': each hole is filled with a single random color - 'inpaint_telea': uses OpenCV Telea inpainting method - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method Default: 0

mask_fill_value ColorType | None

Fill value for dropout regions in the mask. If None, mask regions corresponding to image dropouts are unchanged. Default: None

p float

Probability of applying the transform. Defaults to 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note: Either max_x_length or max_y_length or both must be defined.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/dropout/xy_masking.py Python
class XYMasking(BaseDropout):\n    \"\"\"Applies masking strips to an image, either horizontally (X axis) or vertically (Y axis),\n    simulating occlusions. This transform is useful for training models to recognize images\n    with varied visibility conditions. It's particularly effective for spectrogram images,\n    allowing spectral and frequency masking to improve model robustness.\n\n    At least one of `max_x_length` or `max_y_length` must be specified, dictating the mask's\n    maximum size along each axis.\n\n    Args:\n        num_masks_x (int | tuple[int, int]): Number or range of horizontal regions to mask. Defaults to 0.\n        num_masks_y (int | tuple[int, int]): Number or range of vertical regions to mask. Defaults to 0.\n        mask_x_length (int | tuple[int, int]): Specifies the length of the masks along\n            the X (horizontal) axis. If an integer is provided, it sets a fixed mask length.\n            If a tuple of two integers (min, max) is provided,\n            the mask length is randomly chosen within this range for each mask.\n            This allows for variable-length masks in the horizontal direction.\n        mask_y_length (int | tuple[int, int]): Specifies the height of the masks along\n            the Y (vertical) axis. Similar to `mask_x_length`, an integer sets a fixed mask height,\n            while a tuple (min, max) allows for variable-height masks, chosen randomly\n            within the specified range for each mask. This flexibility facilitates creating masks of various\n            sizes in the vertical direction.\n        fill (ColorType | Literal[\"random\", \"random_uniform\", \"inpaint_telea\", \"inpaint_ns\"]):\n            Value for the dropped pixels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            - 'random': each pixel is filled with random values\n            - 'random_uniform': each hole is filled with a single random color\n            - 'inpaint_telea': uses OpenCV Telea inpainting method\n            - 'inpaint_ns': uses OpenCV Navier-Stokes inpainting method\n            Default: 0\n        mask_fill_value (ColorType | None): Fill value for dropout regions in the mask.\n            If None, mask regions corresponding to image dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Defaults to 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note: Either `max_x_length` or `max_y_length` or both must be defined.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        num_masks_x: NonNegativeIntRangeType\n        num_masks_y: NonNegativeIntRangeType\n        mask_x_length: NonNegativeIntRangeType\n        mask_y_length: NonNegativeIntRangeType\n\n        fill_value: DropoutFillValue | None\n        mask_fill_value: ColorType | None\n\n        fill: DropoutFillValue\n        fill_mask: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def check_mask_length(self) -> Self:\n            if (\n                isinstance(self.mask_x_length, int)\n                and self.mask_x_length <= 0\n                and isinstance(self.mask_y_length, int)\n                and self.mask_y_length <= 0\n            ):\n                msg = \"At least one of `mask_x_length` or `mask_y_length` Should be a positive number.\"\n                raise ValueError(msg)\n\n            if self.fill_value is not None:\n                warn(\"fill_value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.fill_value\n\n            if self.mask_fill_value is not None:\n                warn(\"mask_fill_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_fill_value\n\n            return self\n\n    def __init__(\n        self,\n        num_masks_x: ScaleIntType = 0,\n        num_masks_y: ScaleIntType = 0,\n        mask_x_length: ScaleIntType = 0,\n        mask_y_length: ScaleIntType = 0,\n        fill_value: DropoutFillValue | None = None,\n        mask_fill_value: ColorType | None = None,\n        fill: DropoutFillValue = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, fill=fill, fill_mask=fill_mask)\n        self.num_masks_x = cast(tuple[int, int], num_masks_x)\n        self.num_masks_y = cast(tuple[int, int], num_masks_y)\n\n        self.mask_x_length = cast(tuple[int, int], mask_x_length)\n        self.mask_y_length = cast(tuple[int, int], mask_y_length)\n\n    def validate_mask_length(\n        self,\n        mask_length: tuple[int, int] | None,\n        dimension_size: int,\n        dimension_name: str,\n    ) -> None:\n        \"\"\"Validate the mask length against the corresponding image dimension size.\"\"\"\n        if mask_length is not None:\n            if isinstance(mask_length, (tuple, list)):\n                if mask_length[0] < 0 or mask_length[1] > dimension_size:\n                    raise ValueError(\n                        f\"{dimension_name} range {mask_length} is out of valid range [0, {dimension_size}]\",\n                    )\n            elif mask_length < 0 or mask_length > dimension_size:\n                raise ValueError(f\"{dimension_name} {mask_length} exceeds image {dimension_name} {dimension_size}\")\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        height, width = image_shape\n\n        self.validate_mask_length(self.mask_x_length, width, \"mask_x_length\")\n        self.validate_mask_length(self.mask_y_length, height, \"mask_y_length\")\n\n        masks_x = self.generate_masks(self.num_masks_x, image_shape, self.mask_x_length, axis=\"x\")\n        masks_y = self.generate_masks(self.num_masks_y, image_shape, self.mask_y_length, axis=\"y\")\n\n        holes = np.array(masks_x + masks_y)\n\n        return {\"holes\": holes, \"seed\": self.random_generator.integers(0, 2**32 - 1)}\n\n    def generate_mask_size(self, mask_length: tuple[int, int]) -> int:\n        return self.py_random.randint(*mask_length)\n\n    def generate_masks(\n        self,\n        num_masks: tuple[int, int],\n        image_shape: tuple[int, int],\n        max_length: tuple[int, int] | None,\n        axis: str,\n    ) -> list[tuple[int, int, int, int]]:\n        if max_length is None or max_length == 0 or (isinstance(num_masks, (int, float)) and num_masks == 0):\n            return []\n\n        masks = []\n        num_masks_integer = (\n            num_masks if isinstance(num_masks, int) else self.py_random.randint(num_masks[0], num_masks[1])\n        )\n\n        height, width = image_shape\n\n        for _ in range(num_masks_integer):\n            length = self.generate_mask_size(max_length)\n\n            if axis == \"x\":\n                x_min = self.py_random.randint(0, width - length)\n                y_min = 0\n                x_max, y_max = x_min + length, height\n            else:  # axis == 'y'\n                y_min = self.py_random.randint(0, height - length)\n                x_min = 0\n                x_max, y_max = width, y_min + length\n\n            masks.append((x_min, y_min, x_max, y_max))\n        return masks\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"num_masks_x\",\n            \"num_masks_y\",\n            \"mask_x_length\",\n            \"mask_y_length\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/","title":"Index","text":"
  • Geometric functional transforms (albumentations.augmentations.geometric.functional)
  • Resizing transforms (augmentations.geometric.resize)
  • Rotation transforms (augmentations.geometric.functional)
  • Geometric transforms (augmentations.geometric.transforms)
"},{"location":"api_reference/augmentations/geometric/functional/","title":"Geometric functional transforms (augmentations.geometric.functional)","text":""},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.adjust_padding_by_position","title":"def adjust_padding_by_position (h_top, h_bottom, w_left, w_right, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position.

Source code in albumentations/augmentations/geometric/functional.py Python
def adjust_padding_by_position(\n    h_top: int,\n    h_bottom: int,\n    w_left: int,\n    w_right: int,\n    position: PositionType,\n    py_random: np.random.RandomState,\n) -> tuple[int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position.\"\"\"\n    if position == \"center\":\n        return h_top, h_bottom, w_left, w_right\n\n    if position == \"top_left\":\n        return 0, h_top + h_bottom, 0, w_left + w_right\n\n    if position == \"top_right\":\n        return 0, h_top + h_bottom, w_left + w_right, 0\n\n    if position == \"bottom_left\":\n        return h_top + h_bottom, 0, 0, w_left + w_right\n\n    if position == \"bottom_right\":\n        return h_top + h_bottom, 0, w_left + w_right, 0\n\n    if position == \"random\":\n        h_pad = h_top + h_bottom\n        w_pad = w_left + w_right\n        h_top = py_random.randint(0, h_pad)\n        h_bottom = h_pad - h_top\n        w_left = py_random.randint(0, w_pad)\n        w_right = w_pad - w_left\n        return h_top, h_bottom, w_left, w_right\n\n    raise ValueError(f\"Unknown position: {position}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.almost_equal_intervals","title":"def almost_equal_intervals (n, parts) [view source on GitHub]","text":"

Generates an array of nearly equal integer intervals that sum up to n.

This function divides the number n into parts nearly equal parts. It ensures that the sum of all parts equals n, and the difference between any two parts is at most one. This is useful for distributing a total amount into nearly equal discrete parts.

Parameters:

Name Type Description n int

The total value to be split.

parts int

The number of parts to split into.

Returns:

Type Description np.ndarray

An array of integers where each integer represents the size of a part.

Examples:

Python
>>> almost_equal_intervals(20, 3)\narray([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n>>> almost_equal_intervals(16, 4)\narray([4, 4, 4, 4])  # Splits 16 into four equal parts\n
Source code in albumentations/augmentations/geometric/functional.py Python
def almost_equal_intervals(n: int, parts: int) -> np.ndarray:\n    \"\"\"Generates an array of nearly equal integer intervals that sum up to `n`.\n\n    This function divides the number `n` into `parts` nearly equal parts. It ensures that\n    the sum of all parts equals `n`, and the difference between any two parts is at most one.\n    This is useful for distributing a total amount into nearly equal discrete parts.\n\n    Args:\n        n (int): The total value to be split.\n        parts (int): The number of parts to split into.\n\n    Returns:\n        np.ndarray: An array of integers where each integer represents the size of a part.\n\n    Example:\n        >>> almost_equal_intervals(20, 3)\n        array([7, 7, 6])  # Splits 20 into three parts: 7, 7, and 6\n        >>> almost_equal_intervals(16, 4)\n        array([4, 4, 4, 4])  # Splits 16 into four equal parts\n    \"\"\"\n    part_size, remainder = divmod(n, parts)\n    # Create an array with the base part size and adjust the first `remainder` parts by adding 1\n    return np.array(\n        [part_size + 1 if i < remainder else part_size for i in range(parts)],\n    )\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.apply_affine_to_points","title":"def apply_affine_to_points (points, matrix) [view source on GitHub]","text":"

Apply affine transformation to a set of points.

This function handles potential division by zero by replacing zero values in the homogeneous coordinate with a small epsilon value.

Parameters:

Name Type Description points np.ndarray

Array of points with shape (N, 2).

matrix np.ndarray

3x3 affine transformation matrix.

Returns:

Type Description np.ndarray

Transformed points with shape (N, 2).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"points\")\ndef apply_affine_to_points(points: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply affine transformation to a set of points.\n\n    This function handles potential division by zero by replacing zero values\n    in the homogeneous coordinate with a small epsilon value.\n\n    Args:\n        points (np.ndarray): Array of points with shape (N, 2).\n        matrix (np.ndarray): 3x3 affine transformation matrix.\n\n    Returns:\n        np.ndarray: Transformed points with shape (N, 2).\n    \"\"\"\n    homogeneous_points = np.column_stack([points, np.ones(points.shape[0])])\n    transformed_points = homogeneous_points @ matrix.T\n\n    # Handle potential division by zero\n    epsilon = np.finfo(transformed_points.dtype).eps\n    transformed_points[:, 2] = np.where(\n        np.abs(transformed_points[:, 2]) < epsilon,\n        np.sign(transformed_points[:, 2]) * epsilon,\n        transformed_points[:, 2],\n    )\n\n    return transformed_points[:, :2] / transformed_points[:, 2:]\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine","title":"def bboxes_affine (bboxes, matrix, rotate_method, image_shape, border_mode, output_shape) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes.

For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function: 1. Calculates necessary padding to avoid information loss 2. Applies padding to the bounding boxes 3. Adjusts the transformation matrix to account for padding 4. Applies the affine transformation 5. Validates the transformed bounding boxes

For other border modes, it directly applies the affine transformation without padding.

Parameters:

Name Type Description bboxes np.ndarray

Input bounding boxes

matrix np.ndarray

Affine transformation matrix

rotate_method str

Method for rotating bounding boxes ('largest_box' or 'ellipse')

image_shape Sequence[int]

Shape of the input image

border_mode int

OpenCV border mode

output_shape Sequence[int]

Shape of the output image

Returns:

Type Description np.ndarray

Transformed and normalized bounding boxes

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine(\n    bboxes: np.ndarray,\n    matrix: np.ndarray,\n    rotate_method: Literal[\"largest_box\", \"ellipse\"],\n    image_shape: tuple[int, int],\n    border_mode: int,\n    output_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes.\n\n    For reflection border modes (cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT), this function:\n    1. Calculates necessary padding to avoid information loss\n    2. Applies padding to the bounding boxes\n    3. Adjusts the transformation matrix to account for padding\n    4. Applies the affine transformation\n    5. Validates the transformed bounding boxes\n\n    For other border modes, it directly applies the affine transformation without padding.\n\n    Args:\n        bboxes (np.ndarray): Input bounding boxes\n        matrix (np.ndarray): Affine transformation matrix\n        rotate_method (str): Method for rotating bounding boxes ('largest_box' or 'ellipse')\n        image_shape (Sequence[int]): Shape of the input image\n        border_mode (int): OpenCV border mode\n        output_shape (Sequence[int]): Shape of the output image\n\n    Returns:\n        np.ndarray: Transformed and normalized bounding boxes\n    \"\"\"\n    if is_identity_matrix(matrix):\n        return bboxes\n\n    bboxes = denormalize_bboxes(bboxes, image_shape)\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        bboxes = generate_reflected_bboxes(\n            bboxes,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Apply affine transform\n    if rotate_method == \"largest_box\":\n        transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n    elif rotate_method == \"ellipse\":\n        transformed_bboxes = bboxes_affine_ellipse(bboxes, matrix)\n    else:\n        raise ValueError(f\"Method {rotate_method} is not a valid rotation method.\")\n\n    # Validate and normalize bboxes\n    validated_bboxes = validate_bboxes(transformed_bboxes, output_shape)\n\n    return normalize_bboxes(validated_bboxes, output_shape)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine_ellipse","title":"def bboxes_affine_ellipse (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes using an ellipse approximation method.

This function transforms bounding boxes by approximating each box with an ellipse, transforming points along the ellipse's circumference, and then computing the new bounding box that encloses the transformed ellipse.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The ellipse approximation method can provide a tighter bounding box compared to the largest box method, especially for rotations.
  • 360 points are used to approximate each ellipse, which provides a good balance between accuracy and computational efficiency.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method may be more suitable for objects that are roughly elliptical in shape.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_ellipse(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes using an ellipse approximation method.\n\n    This function transforms bounding boxes by approximating each box with an ellipse,\n    transforming points along the ellipse's circumference, and then computing the\n    new bounding box that encloses the transformed ellipse.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The ellipse approximation method can provide a tighter bounding box compared to the\n          largest box method, especially for rotations.\n        - 360 points are used to approximate each ellipse, which provides a good balance between\n          accuracy and computational efficiency.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method may be more suitable for objects that are roughly elliptical in shape.\n    \"\"\"\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n    bbox_width = (x_max - x_min) / 2\n    bbox_height = (y_max - y_min) / 2\n    center_x = x_min + bbox_width\n    center_y = y_min + bbox_height\n\n    angles = np.arange(0, 360, dtype=np.float32)\n    cos_angles = np.cos(np.radians(angles))\n    sin_angles = np.sin(np.radians(angles))\n\n    # Generate points for all ellipses at once\n    x = bbox_width[:, np.newaxis] * sin_angles + center_x[:, np.newaxis]\n    y = bbox_height[:, np.newaxis] * cos_angles + center_y[:, np.newaxis]\n    points = np.stack([x, y], axis=-1).reshape(-1, 2)\n\n    # Transform all points at once using the helper function\n    transformed_points = apply_affine_to_points(points, matrix)\n\n    transformed_points = transformed_points.reshape(len(bboxes), -1, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_points[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_points[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_points[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_points[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_affine_largest_box","title":"def bboxes_affine_largest_box (bboxes, matrix) [view source on GitHub]","text":"

Apply an affine transformation to bounding boxes and return the largest enclosing boxes.

This function transforms each corner of every bounding box using the given affine transformation matrix, then computes the new bounding boxes that fully enclose the transformed corners.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (N, 4+) where N is the number of bounding boxes. Each row should contain [x_min, y_min, x_max, y_max] followed by any additional attributes (e.g., class labels).

matrix np.ndarray

The 3x3 affine transformation matrix to apply.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as the input. Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by any additional attributes from the input bounding boxes.

Note

  • This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].
  • The resulting bounding boxes are the smallest axis-aligned boxes that completely enclose the transformed original boxes. They may be larger than the minimal possible bounding box if the original box becomes rotated.
  • Any additional attributes beyond the first 4 coordinates are preserved unchanged.
  • This method is called \"largest box\" because it returns the largest axis-aligned box that encloses all corners of the transformed bounding box.

Examples:

Python
>>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n>>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n>>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n>>> print(transformed_bboxes)\n[[ 25.  25.  45.  45.   1.]\n [ 65.  65.  85.  85.   2.]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_affine_largest_box(bboxes: np.ndarray, matrix: np.ndarray) -> np.ndarray:\n    \"\"\"Apply an affine transformation to bounding boxes and return the largest enclosing boxes.\n\n    This function transforms each corner of every bounding box using the given affine transformation\n    matrix, then computes the new bounding boxes that fully enclose the transformed corners.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (N, 4+) where N is the number of\n                             bounding boxes. Each row should contain [x_min, y_min, x_max, y_max]\n                             followed by any additional attributes (e.g., class labels).\n        matrix (np.ndarray): The 3x3 affine transformation matrix to apply.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as the input.\n                    Each row contains [new_x_min, new_y_min, new_x_max, new_y_max] followed by\n                    any additional attributes from the input bounding boxes.\n\n    Note:\n        - This function assumes that the input bounding boxes are in the format [x_min, y_min, x_max, y_max].\n        - The resulting bounding boxes are the smallest axis-aligned boxes that completely\n          enclose the transformed original boxes. They may be larger than the minimal possible\n          bounding box if the original box becomes rotated.\n        - Any additional attributes beyond the first 4 coordinates are preserved unchanged.\n        - This method is called \"largest box\" because it returns the largest axis-aligned box\n          that encloses all corners of the transformed bounding box.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 20, 20, 1], [30, 30, 40, 40, 2]])  # Two boxes with class labels\n        >>> matrix = np.array([[2, 0, 5], [0, 2, 5], [0, 0, 1]])  # Scale by 2 and translate by (5, 5)\n        >>> transformed_bboxes = bboxes_affine_largest_box(bboxes, matrix)\n        >>> print(transformed_bboxes)\n        [[ 25.  25.  45.  45.   1.]\n         [ 65.  65.  85.  85.   2.]]\n    \"\"\"\n    # Extract corners of all bboxes\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    corners = (\n        np.array([[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]]).transpose(2, 0, 1).reshape(-1, 2)\n    )\n\n    # Transform all corners at once\n    transformed_corners = apply_affine_to_points(corners, matrix).reshape(-1, 4, 2)\n\n    # Compute new bounding boxes\n    new_x_min = np.min(transformed_corners[:, :, 0], axis=1)\n    new_x_max = np.max(transformed_corners[:, :, 0], axis=1)\n    new_y_min = np.min(transformed_corners[:, :, 1], axis=1)\n    new_y_max = np.max(transformed_corners[:, :, 1], axis=1)\n\n    return np.column_stack([new_x_min, new_y_min, new_x_max, new_y_max, bboxes[:, 4:]])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_d4","title":"def bboxes_d4 (bboxes, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a bounding box.

The function transforms a bounding box according to the specified group member from the D_4 group. These transformations include rotations and reflections, specified to work on an image's bounding box given its dimensions.

  • bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).
  • group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.
  • BoxInternalType: The transformed bounding box.
  • ValueError: If an invalid group member is specified.

Examples:

  • Applying a 90-degree rotation: bbox_d4((10, 20, 110, 120), 'r90') This would rotate the bounding box 90 degrees within a 100x100 image.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_d4(\n    bboxes: np.ndarray,\n    group_member: D4Type,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a bounding box.\n\n    The function transforms a bounding box according to the specified group member from the `D_4` group.\n    These transformations include rotations and reflections, specified to work on an image's bounding box given\n    its dimensions.\n\n    Parameters:\n    -  bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n    - group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hvt', 'h', 't'.\n\n    Returns:\n    - BoxInternalType: The transformed bounding box.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Applying a 90-degree rotation:\n      `bbox_d4((10, 20, 110, 120), 'r90')`\n      This would rotate the bounding box 90 degrees within a 100x100 image.\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: bboxes_rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: bboxes_rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: bboxes_rot90(x, 3),  # Rotate 270 degrees\n        \"v\": lambda x: bboxes_vflip(x),  # Vertical flip\n        \"hvt\": lambda x: bboxes_transpose(\n            bboxes_rot90(x, 2),\n        ),  # Reflect over anti-diagonal\n        \"h\": lambda x: bboxes_hflip(x),  # Horizontal flip\n        \"t\": lambda x: bboxes_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](bboxes)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_grid_shuffle","title":"def bboxes_grid_shuffle (bboxes, tiles, mapping, image_shape, min_area, min_visibility) [view source on GitHub]","text":"

Apply grid shuffle transformation to bounding boxes.

This function transforms bounding boxes according to a grid shuffle operation. It handles cases where bounding boxes may be split into multiple components after shuffling and applies filtering based on minimum area and visibility requirements.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4+) where N is the number of boxes. Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents optional additional fields (e.g., class_id, score).

tiles np.ndarray

Array of tile coordinates with shape (M, 4) where M is the number of tiles. Each tile is in format [start_y, start_x, end_y, end_x].

mapping list[int]

List of indices defining how tiles should be rearranged. Each index i in the list contains the index of the tile that should be moved to position i.

image_shape tuple[int, int]

Shape of the image as (height, width).

min_area float

Minimum area threshold in pixels. If a component's area after shuffling is smaller than this value, it will be filtered out. If None, no area filtering is applied.

min_visibility float

Minimum visibility ratio threshold in range [0, 1]. Calculated as (component_area / original_area). If a component's visibility is lower than this value, it will be filtered out. If None, no visibility filtering is applied.

Returns:

Type Description np.ndarray

Array of transformed bounding boxes with shape (K, 4+) where K is the number of valid components after shuffling and filtering. The format of each box matches the input format, preserving any additional fields. If no valid components remain after filtering, returns an empty array with shape (0, C) where C matches the input column count.

Note

  • The function converts bboxes to masks before applying the transformation to handle cases where boxes may be split into multiple components.
  • After shuffling, each component is validated against min_area and min_visibility requirements independently.
  • Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and copied to all components derived from the same original bbox.
  • Empty input arrays are handled gracefully and return empty arrays of the appropriate shape.

Examples:

Python
>>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n>>> tiles = np.array([\n...     [0, 0, 50, 50],    # top-left tile\n...     [0, 50, 50, 100],  # top-right tile\n...     [50, 0, 100, 50],  # bottom-left tile\n...     [50, 50, 100, 100] # bottom-right tile\n... ])\n>>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n>>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n>>> # Result may contain multiple boxes if the original box was split\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_grid_shuffle(\n    bboxes: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int],\n    image_shape: tuple[int, int],\n    min_area: float,\n    min_visibility: float,\n) -> np.ndarray:\n    \"\"\"Apply grid shuffle transformation to bounding boxes.\n\n    This function transforms bounding boxes according to a grid shuffle operation. It handles cases\n    where bounding boxes may be split into multiple components after shuffling and applies\n    filtering based on minimum area and visibility requirements.\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4+) where N is the number of boxes.\n               Each box is in format [x_min, y_min, x_max, y_max, ...], where ... represents\n               optional additional fields (e.g., class_id, score).\n        tiles: Array of tile coordinates with shape (M, 4) where M is the number of tiles.\n               Each tile is in format [start_y, start_x, end_y, end_x].\n        mapping: List of indices defining how tiles should be rearranged. Each index i in the list\n                contains the index of the tile that should be moved to position i.\n        image_shape: Shape of the image as (height, width).\n        min_area: Minimum area threshold in pixels. If a component's area after shuffling is\n                 smaller than this value, it will be filtered out. If None, no area filtering\n                 is applied.\n        min_visibility: Minimum visibility ratio threshold in range [0, 1]. Calculated as\n                       (component_area / original_area). If a component's visibility is lower\n                       than this value, it will be filtered out. If None, no visibility\n                       filtering is applied.\n\n    Returns:\n        np.ndarray: Array of transformed bounding boxes with shape (K, 4+) where K is the\n                   number of valid components after shuffling and filtering. The format of\n                   each box matches the input format, preserving any additional fields.\n                   If no valid components remain after filtering, returns an empty array\n                   with shape (0, C) where C matches the input column count.\n\n    Note:\n        - The function converts bboxes to masks before applying the transformation to handle\n          cases where boxes may be split into multiple components.\n        - After shuffling, each component is validated against min_area and min_visibility\n          requirements independently.\n        - Additional bbox fields (beyond x_min, y_min, x_max, y_max) are preserved and\n          copied to all components derived from the same original bbox.\n        - Empty input arrays are handled gracefully and return empty arrays of the\n          appropriate shape.\n\n    Example:\n        >>> bboxes = np.array([[10, 10, 90, 90]])  # Single box crossing multiple tiles\n        >>> tiles = np.array([\n        ...     [0, 0, 50, 50],    # top-left tile\n        ...     [0, 50, 50, 100],  # top-right tile\n        ...     [50, 0, 100, 50],  # bottom-left tile\n        ...     [50, 50, 100, 100] # bottom-right tile\n        ... ])\n        >>> mapping = [3, 2, 1, 0]  # Rotate tiles counter-clockwise\n        >>> result = bboxes_grid_shuffle(bboxes, tiles, mapping, (100, 100), 100, 0.2)\n        >>> # Result may contain multiple boxes if the original box was split\n    \"\"\"\n    # Convert bboxes to masks\n    masks = masks_from_bboxes(bboxes, image_shape)\n\n    # Apply grid shuffle to each mask and handle split components\n    all_component_masks = []\n    extra_bbox_data = []  # Store additional bbox data for each component\n\n    for idx, mask in enumerate(masks):\n        original_area = np.sum(mask)  # Get original mask area\n\n        # Shuffle the mask\n        shuffled_mask = swap_tiles_on_image(mask, tiles, mapping)\n\n        # Find connected components\n        num_components, components = cv2.connectedComponents(\n            shuffled_mask.astype(np.uint8),\n        )\n\n        # For each component, create a separate binary mask\n        for comp_idx in range(1, num_components):  # Skip background (0)\n            component_mask = (components == comp_idx).astype(np.uint8)\n\n            # Calculate area and visibility ratio\n            component_area = np.sum(component_mask)\n            # Check if component meets minimum requirements\n            if is_valid_component(\n                component_area,\n                original_area,\n                min_area,\n                min_visibility,\n            ):\n                all_component_masks.append(component_mask)\n                # Append additional bbox data for this component\n                if bboxes.shape[1] > NUM_BBOXES_COLUMNS_IN_ALBUMENTATIONS:\n                    extra_bbox_data.append(bboxes[idx, 4:])\n\n    # Convert all component masks to bboxes\n    if all_component_masks:\n        all_component_masks = np.array(all_component_masks)\n        shuffled_bboxes = bboxes_from_masks(all_component_masks)\n\n        # Add back additional bbox data if present\n        if extra_bbox_data:\n            extra_bbox_data = np.array(extra_bbox_data)\n            return np.column_stack([shuffled_bboxes, extra_bbox_data])\n    else:\n        # Handle case where no valid components were found\n        return np.zeros((0, bboxes.shape[1]), dtype=bboxes.dtype)\n\n    return shuffled_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_hflip","title":"def bboxes_hflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes horizontally around the y-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of horizontally flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_hflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally around the y-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of horizontally flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 0] = 1 - bboxes[:, 2]  # new x_min = 1 - x_max\n    flipped_bboxes[:, 2] = 1 - bboxes[:, 0]  # new x_max = 1 - x_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_rot90","title":"def bboxes_rot90 (bboxes, factor) [view source on GitHub]","text":"

Rotates bounding boxes by 90 degrees CCW (see np.rot90)

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

factor Literal[0, 1, 2, 3]

Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.

Returns:

Type Description np.ndarray

A numpy array of rotated bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_rot90(bboxes: np.ndarray, factor: Literal[0, 1, 2, 3]) -> np.ndarray:\n    \"\"\"Rotates bounding boxes by 90 degrees CCW (see np.rot90)\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n        factor: Number of CCW rotations. Must be in set {0, 1, 2, 3} See np.rot90.\n\n    Returns:\n        np.ndarray: A numpy array of rotated bounding boxes with the same shape as input.\n    \"\"\"\n    if factor == 0:\n        return bboxes\n\n    rotated_bboxes = bboxes.copy()\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    if factor == 1:\n        rotated_bboxes[:, 0] = y_min\n        rotated_bboxes[:, 1] = 1 - x_max\n        rotated_bboxes[:, 2] = y_max\n        rotated_bboxes[:, 3] = 1 - x_min\n    elif factor == ROT90_180_FACTOR:\n        rotated_bboxes[:, 0] = 1 - x_max\n        rotated_bboxes[:, 1] = 1 - y_max\n        rotated_bboxes[:, 2] = 1 - x_min\n        rotated_bboxes[:, 3] = 1 - y_min\n    elif factor == ROT90_270_FACTOR:\n        rotated_bboxes[:, 0] = 1 - y_max\n        rotated_bboxes[:, 1] = x_min\n        rotated_bboxes[:, 2] = 1 - y_min\n        rotated_bboxes[:, 3] = x_max\n\n    return rotated_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_transpose","title":"def bboxes_transpose (bboxes) [view source on GitHub]","text":"

Transpose bounding boxes by swapping x and y coordinates.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of transposed bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_transpose(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Transpose bounding boxes by swapping x and y coordinates.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of transposed bounding boxes with the same shape as input.\n    \"\"\"\n    transposed_bboxes = bboxes.copy()\n    transposed_bboxes[:, [0, 1, 2, 3]] = bboxes[:, [1, 0, 3, 2]]\n\n    return transposed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.bboxes_vflip","title":"def bboxes_vflip (bboxes) [view source on GitHub]","text":"

Flip bounding boxes vertically around the x-axis.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).

Returns:

Type Description np.ndarray

A numpy array of vertically flipped bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef bboxes_vflip(bboxes: np.ndarray) -> np.ndarray:\n    \"\"\"Flip bounding boxes vertically around the x-axis.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n                Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n\n    Returns:\n        np.ndarray: A numpy array of vertically flipped bounding boxes with the same shape as input.\n    \"\"\"\n    flipped_bboxes = bboxes.copy()\n    flipped_bboxes[:, 1] = 1 - bboxes[:, 3]  # new y_min = 1 - y_max\n    flipped_bboxes[:, 3] = 1 - bboxes[:, 1]  # new y_max = 1 - y_min\n\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.calculate_affine_transform_padding","title":"def calculate_affine_transform_padding (matrix, image_shape) [view source on GitHub]","text":"

Calculate the necessary padding for an affine transformation to avoid empty spaces.

Source code in albumentations/augmentations/geometric/functional.py Python
def calculate_affine_transform_padding(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate the necessary padding for an affine transformation to avoid empty spaces.\"\"\"\n    height, width = image_shape[:2]\n\n    # Check for identity transform\n    if is_identity_matrix(matrix):\n        return (0, 0, 0, 0)\n\n    # Original corners\n    corners = np.array([[0, 0], [width, 0], [width, height], [0, height]])\n\n    # Transform corners\n    transformed_corners = apply_affine_to_points(corners, matrix)\n\n    # Ensure transformed_corners is 2D\n    transformed_corners = transformed_corners.reshape(-1, 2)\n\n    # Find box that includes both original and transformed corners\n    all_corners = np.vstack((corners, transformed_corners))\n    min_x, min_y = all_corners.min(axis=0)\n    max_x, max_y = all_corners.max(axis=0)\n\n    # Compute the inverse transform\n    inverse_matrix = np.linalg.inv(matrix)\n\n    # Apply inverse transform to all corners of the bounding box\n    bbox_corners = np.array(\n        [[min_x, min_y], [max_x, min_y], [max_x, max_y], [min_x, max_y]],\n    )\n    inverse_corners = apply_affine_to_points(bbox_corners, inverse_matrix).reshape(\n        -1,\n        2,\n    )\n\n    min_x, min_y = inverse_corners.min(axis=0)\n    max_x, max_y = inverse_corners.max(axis=0)\n\n    pad_left = max(0, math.ceil(0 - min_x))\n    pad_right = max(0, math.ceil(max_x - width))\n    pad_top = max(0, math.ceil(0 - min_y))\n    pad_bottom = max(0, math.ceil(max_y - height))\n\n    return pad_left, pad_right, pad_top, pad_bottom\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.center","title":"def center (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates if image. Used by images, masks and keypoints.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates if image. Used by images, masks and keypoints.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2 - 0.5, height / 2 - 0.5\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.center_bbox","title":"def center_bbox (image_shape) [view source on GitHub]","text":"

Calculate the center coordinates for of image for bounding boxes.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image.

Returns:

Type Description tuple[float, float]

center_x, center_y

Source code in albumentations/augmentations/geometric/functional.py Python
def center_bbox(image_shape: tuple[int, int]) -> tuple[float, float]:\n    \"\"\"Calculate the center coordinates for of image for bounding boxes.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image.\n\n    Returns:\n        tuple[float, float]: center_x, center_y\n    \"\"\"\n    height, width = image_shape[:2]\n    return width / 2, height / 2\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.compute_tps_weights","title":"def compute_tps_weights (src_points, dst_points) [view source on GitHub]","text":"

Compute Thin Plate Spline weights.

Parameters:

Name Type Description src_points np.ndarray

Source control points with shape (num_points, 2)

dst_points np.ndarray

Destination control points with shape (num_points, 2)

Returns:

Type Description tuple of
  • nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)
  • affine_weights: Weights for affine transformation (3, 2) [constant term, x scale/shear, y scale/shear]

Note

The TPS interpolation is decomposed into: 1. Nonlinear part (controlled by kernel weights) 2. Affine part (global scaling, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_tps_weights(\n    src_points: np.ndarray,\n    dst_points: np.ndarray,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute Thin Plate Spline weights.\n\n    Args:\n        src_points: Source control points with shape (num_points, 2)\n        dst_points: Destination control points with shape (num_points, 2)\n\n    Returns:\n        tuple of:\n        - nonlinear_weights: TPS kernel weights for nonlinear deformation (num_points, 2)\n        - affine_weights: Weights for affine transformation (3, 2)\n            [constant term, x scale/shear, y scale/shear]\n\n    Note:\n        The TPS interpolation is decomposed into:\n        1. Nonlinear part (controlled by kernel weights)\n        2. Affine part (global scaling, rotation, translation)\n    \"\"\"\n    num_points = src_points.shape[0]\n\n    # Compute pairwise distances\n    distances = np.linalg.norm(src_points[:, None] - src_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    # Add small epsilon to avoid log(0)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Construct affine terms matrix [1, x, y]\n    affine_terms = np.ones((num_points, 3))\n    affine_terms[:, 1:] = src_points\n\n    # Build system matrix\n    system_matrix = np.zeros((num_points + 3, num_points + 3))\n    system_matrix[:num_points, :num_points] = kernel_matrix\n    system_matrix[:num_points, num_points:] = affine_terms\n    system_matrix[num_points:, :num_points] = affine_terms.T\n\n    # Right-hand side of the system\n    target_coords = np.zeros((num_points + 3, 2))\n    target_coords[:num_points] = dst_points\n\n    # Solve the system for both x and y coordinates\n    all_weights = np.linalg.solve(system_matrix, target_coords)\n\n    # Split weights into nonlinear and affine components\n    nonlinear_weights = all_weights[:num_points]\n    affine_weights = all_weights[num_points:]\n\n    return nonlinear_weights, affine_weights\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.compute_transformed_image_bounds","title":"def compute_transformed_image_bounds (matrix, image_shape) [view source on GitHub]","text":"

Compute the bounds of an image after applying an affine transformation.

Parameters:

Name Type Description matrix np.ndarray

The 3x3 affine transformation matrix.

image_shape Tuple[int, int]

The shape of the image as (height, width).

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing: - min_coords: An array with the minimum x and y coordinates. - max_coords: An array with the maximum x and y coordinates.

Source code in albumentations/augmentations/geometric/functional.py Python
def compute_transformed_image_bounds(\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Compute the bounds of an image after applying an affine transformation.\n\n    Args:\n        matrix (np.ndarray): The 3x3 affine transformation matrix.\n        image_shape (Tuple[int, int]): The shape of the image as (height, width).\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing:\n            - min_coords: An array with the minimum x and y coordinates.\n            - max_coords: An array with the maximum x and y coordinates.\n    \"\"\"\n    height, width = image_shape[:2]\n\n    # Define the corners of the image\n    corners = np.array([[0, 0, 1], [width, 0, 1], [width, height, 1], [0, height, 1]])\n\n    # Transform the corners\n    transformed_corners = corners @ matrix.T\n    transformed_corners = transformed_corners[:, :2] / transformed_corners[:, 2:]\n\n    # Calculate the bounding box of the transformed corners\n    min_coords = np.floor(transformed_corners.min(axis=0)).astype(int)\n    max_coords = np.ceil(transformed_corners.max(axis=0)).astype(int)\n\n    return min_coords, max_coords\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_affine_transformation_matrix","title":"def create_affine_transformation_matrix (translate, shear, scale, rotate, shift) [view source on GitHub]","text":"

Create an affine transformation matrix combining translation, shear, scale, and rotation.

Parameters:

Name Type Description translate dict[str, float]

Translation in x and y directions.

shear dict[str, float]

Shear in x and y directions (in degrees).

scale dict[str, float]

Scale factors for x and y directions.

rotate float

Rotation angle in degrees.

shift tuple[float, float]

Shift to apply before and after transformations.

Returns:

Type Description np.ndarray

The resulting 3x3 affine transformation matrix.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_affine_transformation_matrix(\n    translate: XYInt,\n    shear: XYFloat,\n    scale: XYFloat,\n    rotate: float,\n    shift: tuple[float, float],\n) -> np.ndarray:\n    \"\"\"Create an affine transformation matrix combining translation, shear, scale, and rotation.\n\n    Args:\n        translate (dict[str, float]): Translation in x and y directions.\n        shear (dict[str, float]): Shear in x and y directions (in degrees).\n        scale (dict[str, float]): Scale factors for x and y directions.\n        rotate (float): Rotation angle in degrees.\n        shift (tuple[float, float]): Shift to apply before and after transformations.\n\n    Returns:\n        np.ndarray: The resulting 3x3 affine transformation matrix.\n    \"\"\"\n    # Convert angles to radians\n    rotate_rad = np.deg2rad(rotate % 360)\n\n    shear_x_rad = np.deg2rad(shear[\"x\"])\n    shear_y_rad = np.deg2rad(shear[\"y\"])\n\n    # Create individual transformation matrices\n    # 1. Shift to top-left\n    m_shift_topleft = np.array([[1, 0, -shift[0]], [0, 1, -shift[1]], [0, 0, 1]])\n\n    # 2. Scale\n    m_scale = np.array([[scale[\"x\"], 0, 0], [0, scale[\"y\"], 0], [0, 0, 1]])\n\n    # 3. Rotation\n    m_rotate = np.array(\n        [\n            [np.cos(rotate_rad), np.sin(rotate_rad), 0],\n            [-np.sin(rotate_rad), np.cos(rotate_rad), 0],\n            [0, 0, 1],\n        ],\n    )\n\n    # 4. Shear\n    m_shear = np.array(\n        [[1, np.tan(shear_x_rad), 0], [np.tan(shear_y_rad), 1, 0], [0, 0, 1]],\n    )\n\n    # 5. Translation\n    m_translate = np.array([[1, 0, translate[\"x\"]], [0, 1, translate[\"y\"]], [0, 0, 1]])\n\n    # 6. Shift back to center\n    m_shift_center = np.array([[1, 0, shift[0]], [0, 1, shift[1]], [0, 0, 1]])\n\n    # Combine all transformations\n    # The order is important: transformations are applied from right to left\n    m = m_shift_center @ m_translate @ m_shear @ m_rotate @ m_scale @ m_shift_topleft\n\n    # Ensure the last row is exactly [0, 0, 1]\n    m[2] = [0, 0, 1]\n\n    return m\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_piecewise_affine_maps","title":"def create_piecewise_affine_maps (image_shape, grid, scale, absolute_scale, random_generator) [view source on GitHub]","text":"

Create maps for piecewise affine transformation using OpenCV's remap function.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_piecewise_affine_maps(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    scale: float,\n    absolute_scale: bool,\n    random_generator: np.random.Generator,\n) -> tuple[np.ndarray | None, np.ndarray | None]:\n    \"\"\"Create maps for piecewise affine transformation using OpenCV's remap function.\"\"\"\n    height, width = image_shape[:2]\n    nb_rows, nb_cols = grid\n\n    # Input validation\n    if height <= 0 or width <= 0 or nb_rows <= 0 or nb_cols <= 0:\n        raise ValueError(\"Dimensions must be positive\")\n    if scale <= 0:\n        return None, None\n\n    # Create source points grid\n    y = np.linspace(0, height - 1, nb_rows, dtype=np.float32)\n    x = np.linspace(0, width - 1, nb_cols, dtype=np.float32)\n    xx_src, yy_src = np.meshgrid(x, y)\n\n    # Initialize destination maps at full resolution\n    map_x = np.zeros((height, width), dtype=np.float32)\n    map_y = np.zeros((height, width), dtype=np.float32)\n\n    # Generate jitter for control points\n    jitter_scale = scale / 3 if absolute_scale else scale * min(width, height) / 3\n\n    jitter = random_generator.normal(0, jitter_scale, (nb_rows, nb_cols, 2)).astype(\n        np.float32,\n    )\n\n    # Create control points with jitter\n    control_points = np.zeros((nb_rows * nb_cols, 4), dtype=np.float32)\n    for i in range(nb_rows):\n        for j in range(nb_cols):\n            idx = i * nb_cols + j\n            # Source points\n            control_points[idx, 0] = xx_src[i, j]\n            control_points[idx, 1] = yy_src[i, j]\n            # Destination points with jitter\n            control_points[idx, 2] = np.clip(\n                xx_src[i, j] + jitter[i, j, 1],\n                0,\n                width - 1,\n            )\n            control_points[idx, 3] = np.clip(\n                yy_src[i, j] + jitter[i, j, 0],\n                0,\n                height - 1,\n            )\n\n    # Create full resolution maps\n    for i in range(height):\n        for j in range(width):\n            # Find nearest control points and interpolate\n            dx = j - control_points[:, 0]\n            dy = i - control_points[:, 1]\n            dist = dx * dx + dy * dy\n            weights = 1 / (dist + 1e-8)\n            weights = weights / np.sum(weights)\n\n            map_x[i, j] = np.sum(weights * control_points[:, 2])\n            map_y[i, j] = np.sum(weights * control_points[:, 3])\n\n    # Ensure output is within bounds\n    map_x = np.clip(map_x, 0, width - 1, out=map_x)\n    map_y = np.clip(map_y, 0, height - 1, out=map_y)\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.create_shape_groups","title":"def create_shape_groups (tiles) [view source on GitHub]","text":"

Groups tiles by their shape and stores the indices for each shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def create_shape_groups(tiles: np.ndarray) -> dict[tuple[int, int], list[int]]:\n    \"\"\"Groups tiles by their shape and stores the indices for each shape.\"\"\"\n    shape_groups = defaultdict(list)\n    for index, (start_y, start_x, end_y, end_x) in enumerate(tiles):\n        shape = (end_y - start_y, end_x - start_x)\n        shape_groups[shape].append(index)\n    return shape_groups\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.d4","title":"def d4 (img, group_member) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to an image array.

This function manipulates an image using transformations such as rotations and flips, corresponding to the D_4 dihedral group symmetry operations. Each transformation is identified by a unique group member code.

  • img (np.ndarray): The input image array to transform.
  • group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:
  • 'e': Identity (no transformation).
  • 'r90': Rotate 90 degrees counterclockwise.
  • 'r180': Rotate 180 degrees.
  • 'r270': Rotate 270 degrees counterclockwise.
  • 'v': Vertical flip.
  • 'hvt': Transpose over second diagonal
  • 'h': Horizontal flip.
  • 't': Transpose (reflect over the main diagonal).
  • np.ndarray: The transformed image array.
  • ValueError: If an invalid group member is specified.

Examples:

  • Rotating an image by 90 degrees: transformed_image = d4(original_image, 'r90')
  • Applying a horizontal flip to an image: transformed_image = d4(original_image, 'h')
Source code in albumentations/augmentations/geometric/functional.py Python
def d4(img: np.ndarray, group_member: D4Type) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to an image array.\n\n    This function manipulates an image using transformations such as rotations and flips,\n    corresponding to the `D_4` dihedral group symmetry operations.\n    Each transformation is identified by a unique group member code.\n\n    Parameters:\n    - img (np.ndarray): The input image array to transform.\n    - group_member (D4Type): A string identifier indicating the specific transformation to apply. Valid codes include:\n      - 'e': Identity (no transformation).\n      - 'r90': Rotate 90 degrees counterclockwise.\n      - 'r180': Rotate 180 degrees.\n      - 'r270': Rotate 270 degrees counterclockwise.\n      - 'v': Vertical flip.\n      - 'hvt': Transpose over second diagonal\n      - 'h': Horizontal flip.\n      - 't': Transpose (reflect over the main diagonal).\n\n    Returns:\n    - np.ndarray: The transformed image array.\n\n    Raises:\n    - ValueError: If an invalid group member is specified.\n\n    Examples:\n    - Rotating an image by 90 degrees:\n      `transformed_image = d4(original_image, 'r90')`\n    - Applying a horizontal flip to an image:\n      `transformed_image = d4(original_image, 'h')`\n    \"\"\"\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: rot90(x, 1),  # Rotate 90 degrees\n        \"r180\": lambda x: rot90(x, 2),  # Rotate 180 degrees\n        \"r270\": lambda x: rot90(x, 3),  # Rotate 270 degrees\n        \"v\": vflip,  # Vertical flip\n        \"hvt\": lambda x: transpose(rot90(x, 2)),  # Reflect over anti-diagonal\n        \"h\": hflip,  # Horizontal flip\n        \"t\": transpose,  # Transpose (reflect over main diagonal)\n    }\n\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](img)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.distort_image","title":"def distort_image (image, generated_mesh, interpolation) [view source on GitHub]","text":"

Apply perspective distortion to an image based on a generated mesh.

This function applies a perspective transformation to each cell of the image defined by the generated mesh. The distortion is applied using OpenCV's perspective transformation and blending techniques.

Parameters:

Name Type Description image np.ndarray

The input image to be distorted. Can be a 2D grayscale image or a 3D color image.

generated_mesh np.ndarray

A 2D array where each row represents a quadrilateral cell as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4]. The first four values define the source rectangle, and the last eight values define the destination quadrilateral.

interpolation int

Interpolation method to be used in the perspective transformation. Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).

Returns:

Type Description np.ndarray

The distorted image with the same shape and dtype as the input image.

Note

  • The function preserves the channel dimension of the input image.
  • Each cell of the generated mesh is transformed independently and then blended into the output image.
  • The distortion is applied using perspective transformation, which allows for more complex distortions compared to affine transformations.

Examples:

Python
>>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n>>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n>>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n>>> distorted.shape\n(100, 100, 3)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@preserve_channel_dim\ndef distort_image(\n    image: np.ndarray,\n    generated_mesh: np.ndarray,\n    interpolation: int,\n) -> np.ndarray:\n    \"\"\"Apply perspective distortion to an image based on a generated mesh.\n\n    This function applies a perspective transformation to each cell of the image defined by the\n    generated mesh. The distortion is applied using OpenCV's perspective transformation and\n    blending techniques.\n\n    Args:\n        image (np.ndarray): The input image to be distorted. Can be a 2D grayscale image or a\n                            3D color image.\n        generated_mesh (np.ndarray): A 2D array where each row represents a quadrilateral cell\n                                    as [x1, y1, x2, y2, dst_x1, dst_y1, dst_x2, dst_y2, dst_x3, dst_y3, dst_x4, dst_y4].\n                                    The first four values define the source rectangle, and the last eight values\n                                    define the destination quadrilateral.\n        interpolation (int): Interpolation method to be used in the perspective transformation.\n                             Should be one of the OpenCV interpolation flags (e.g., cv2.INTER_LINEAR).\n\n    Returns:\n        np.ndarray: The distorted image with the same shape and dtype as the input image.\n\n    Note:\n        - The function preserves the channel dimension of the input image.\n        - Each cell of the generated mesh is transformed independently and then blended into the output image.\n        - The distortion is applied using perspective transformation, which allows for more complex\n          distortions compared to affine transformations.\n\n    Example:\n        >>> image = np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8)\n        >>> mesh = np.array([[0, 0, 50, 50, 5, 5, 45, 5, 45, 45, 5, 45]])\n        >>> distorted = distort_image(image, mesh, cv2.INTER_LINEAR)\n        >>> distorted.shape\n        (100, 100, 3)\n    \"\"\"\n    distorted_image = np.zeros_like(image)\n\n    for mesh in generated_mesh:\n        # Extract source rectangle and destination quadrilateral\n        x1, y1, x2, y2 = mesh[:4]  # Source rectangle\n        dst_quad = mesh[4:].reshape(4, 2)  # Destination quadrilateral\n\n        # Convert source rectangle to quadrilateral\n        src_quad = np.array(\n            [\n                [x1, y1],  # Top-left\n                [x2, y1],  # Top-right\n                [x2, y2],  # Bottom-right\n                [x1, y2],  # Bottom-left\n            ],\n            dtype=np.float32,\n        )\n\n        # Calculate Perspective transformation matrix\n        perspective_mat = cv2.getPerspectiveTransform(src_quad, dst_quad)\n\n        # Apply Perspective transformation\n        warped = cv2.warpPerspective(\n            image,\n            perspective_mat,\n            (image.shape[1], image.shape[0]),\n            flags=interpolation,\n        )\n\n        # Create mask for the transformed region\n        mask = np.zeros(image.shape[:2], dtype=np.uint8)\n        cv2.fillConvexPoly(mask, np.int32(dst_quad), 255)\n\n        # Copy only the warped quadrilateral area to the output image\n        distorted_image = cv2.copyTo(warped, mask, distorted_image)\n\n    return distorted_image\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.find_keypoint","title":"def find_keypoint (position, distance_map, threshold, inverted) [view source on GitHub]","text":"

Determine if a valid keypoint can be found at the given position.

Source code in albumentations/augmentations/geometric/functional.py Python
def find_keypoint(\n    position: tuple[int, int],\n    distance_map: np.ndarray,\n    threshold: float | None,\n    inverted: bool,\n) -> tuple[float, float] | None:\n    \"\"\"Determine if a valid keypoint can be found at the given position.\"\"\"\n    y, x = position\n    value = distance_map[y, x]\n    if not inverted and threshold is not None and value >= threshold:\n        return None\n    if inverted and threshold is not None and value <= threshold:\n        return None\n    return float(x), float(y)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.flip_bboxes","title":"def flip_bboxes (bboxes, flip_horizontal=False, flip_vertical=False, image_shape=(0, 0)) [view source on GitHub]","text":"

Flip bounding boxes horizontally and/or vertically.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where each row is [x_min, y_min, x_max, y_max, ...].

flip_horizontal bool

Whether to flip horizontally.

flip_vertical bool

Whether to flip vertically.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Flipped bounding boxes.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef flip_bboxes(\n    bboxes: np.ndarray,\n    flip_horizontal: bool = False,\n    flip_vertical: bool = False,\n    image_shape: tuple[int, int] = (0, 0),\n) -> np.ndarray:\n    \"\"\"Flip bounding boxes horizontally and/or vertically.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where each row is\n            [x_min, y_min, x_max, y_max, ...].\n        flip_horizontal (bool): Whether to flip horizontally.\n        flip_vertical (bool): Whether to flip vertically.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Flipped bounding boxes.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    flipped_bboxes = bboxes.copy()\n    if flip_horizontal:\n        flipped_bboxes[:, [0, 2]] = cols - flipped_bboxes[:, [2, 0]]\n    if flip_vertical:\n        flipped_bboxes[:, [1, 3]] = rows - flipped_bboxes[:, [3, 1]]\n    return flipped_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.from_distance_maps","title":"def from_distance_maps (distance_maps, inverted, if_not_found_coords=None, threshold=None) [view source on GitHub]","text":"

Convert distance maps back to keypoints coordinates.

This function is the inverse of to_distance_maps. It takes distance maps generated for a set of keypoints and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps, and can handle cases where keypoints are not found or fall outside a specified threshold.

Parameters:

Name Type Description distance_maps np.ndarray

A 3D numpy array of shape (height, width, nb_keypoints) containing distance maps for each keypoint. Each channel represents the distance map for one keypoint.

inverted bool

If True, treats the distance maps as inverted (where higher values indicate closer proximity to keypoints). If False, treats them as regular distance maps (where lower values indicate closer proximity).

if_not_found_coords Sequence[int] | dict[str, Any] | None

Coordinates to use for keypoints that are not found or fall outside the threshold. Can be: - None: Drop keypoints that are not found. - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints. - Dict with 'x' and 'y' keys: Use these values for not found keypoints. Defaults to None.

threshold float | None

A threshold value to determine valid keypoints. For inverted maps, values >= threshold are considered valid. For regular maps, values <= threshold are considered valid. If None, all keypoints are considered valid. Defaults to None.

Returns:

Type Description np.ndarray

A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates of the reconstructed keypoints. If drop_if_not_found is True (derived from if_not_found_coords), the output may have fewer rows than input keypoints.

Exceptions:

Type Description ValueError

If the input distance_maps is not a 3D array.

Notes

  • The function uses vectorized operations for improved performance, especially with large numbers of keypoints.
  • When threshold is None, all keypoints are considered valid, and if_not_found_coords is not used.
  • The function assumes that the input distance maps are properly normalized and scaled according to the original image dimensions.

Examples:

Python
>>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n>>> inverted = True\n>>> if_not_found_coords = [0, 0]\n>>> threshold = 0.5\n>>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n>>> print(keypoints.shape)\n(3, 2)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def from_distance_maps(\n    distance_maps: np.ndarray,\n    inverted: bool,\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None = None,\n    threshold: float | None = None,\n) -> np.ndarray:\n    \"\"\"Convert distance maps back to keypoints coordinates.\n\n    This function is the inverse of `to_distance_maps`. It takes distance maps generated for a set of keypoints\n    and reconstructs the original keypoint coordinates. The function supports both regular and inverted distance maps,\n    and can handle cases where keypoints are not found or fall outside a specified threshold.\n\n    Args:\n        distance_maps (np.ndarray): A 3D numpy array of shape (height, width, nb_keypoints) containing\n            distance maps for each keypoint. Each channel represents the distance map for one keypoint.\n        inverted (bool): If True, treats the distance maps as inverted (where higher values indicate\n            closer proximity to keypoints). If False, treats them as regular distance maps (where lower\n            values indicate closer proximity).\n        if_not_found_coords (Sequence[int] | dict[str, Any] | None, optional): Coordinates to use for\n            keypoints that are not found or fall outside the threshold. Can be:\n            - None: Drop keypoints that are not found.\n            - Sequence of two integers: Use these as (x, y) coordinates for not found keypoints.\n            - Dict with 'x' and 'y' keys: Use these values for not found keypoints.\n            Defaults to None.\n        threshold (float | None, optional): A threshold value to determine valid keypoints. For inverted\n            maps, values >= threshold are considered valid. For regular maps, values <= threshold are\n            considered valid. If None, all keypoints are considered valid. Defaults to None.\n\n    Returns:\n        np.ndarray: A 2D numpy array of shape (nb_keypoints, 2) containing the (x, y) coordinates\n        of the reconstructed keypoints. If `drop_if_not_found` is True (derived from if_not_found_coords),\n        the output may have fewer rows than input keypoints.\n\n    Raises:\n        ValueError: If the input `distance_maps` is not a 3D array.\n\n    Notes:\n        - The function uses vectorized operations for improved performance, especially with large numbers of keypoints.\n        - When `threshold` is None, all keypoints are considered valid, and `if_not_found_coords` is not used.\n        - The function assumes that the input distance maps are properly normalized and scaled according to the\n          original image dimensions.\n\n    Example:\n        >>> distance_maps = np.random.rand(100, 100, 3)  # 3 keypoints\n        >>> inverted = True\n        >>> if_not_found_coords = [0, 0]\n        >>> threshold = 0.5\n        >>> keypoints = from_distance_maps(distance_maps, inverted, if_not_found_coords, threshold)\n        >>> print(keypoints.shape)\n        (3, 2)\n    \"\"\"\n    if distance_maps.ndim != NUM_MULTI_CHANNEL_DIMENSIONS:\n        msg = f\"Expected three-dimensional input, got {distance_maps.ndim} dimensions and shape {distance_maps.shape}.\"\n        raise ValueError(msg)\n    height, width, nb_keypoints = distance_maps.shape\n\n    drop_if_not_found, if_not_found_x, if_not_found_y = validate_if_not_found_coords(\n        if_not_found_coords,\n    )\n\n    # Find the indices of max/min values for all keypoints at once\n    if inverted:\n        hitidx_flat = np.argmax(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n    else:\n        hitidx_flat = np.argmin(\n            distance_maps.reshape(height * width, nb_keypoints),\n            axis=0,\n        )\n\n    # Convert flat indices to 2D coordinates\n    hitidx_y, hitidx_x = np.unravel_index(hitidx_flat, (height, width))\n\n    # Create keypoints array\n    keypoints = np.column_stack((hitidx_x, hitidx_y)).astype(float)\n\n    if threshold is not None:\n        # Check threshold condition\n        if inverted:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] >= threshold\n        else:\n            valid_mask = distance_maps[hitidx_y, hitidx_x, np.arange(nb_keypoints)] <= threshold\n\n        if not drop_if_not_found:\n            # Replace invalid keypoints with if_not_found_coords\n            keypoints[~valid_mask] = [if_not_found_x, if_not_found_y]\n        else:\n            # Keep only valid keypoints\n            return keypoints[valid_mask]\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_displacement_fields","title":"def generate_displacement_fields (image_shape, alpha, sigma, same_dxdy, kernel_size, random_generator, noise_distribution) [view source on GitHub]","text":"

Generate displacement fields for elastic transform.

Parameters:

Name Type Description image_shape tuple[int, int]

Shape of the image (height, width)

alpha float

Scaling factor for displacement

sigma float

Standard deviation for Gaussian blur

same_dxdy bool

Whether to use same displacement field for both directions

kernel_size tuple[int, int]

Size of Gaussian blur kernel

random_generator np.random.Generator

NumPy random number generator

noise_distribution Literal['gaussian', 'uniform']

Type of noise distribution to use (\"gaussian\" or \"uniform\")

Returns:

Type Description tuple

(dx, dy) displacement fields

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_displacement_fields(\n    image_shape: tuple[int, int],\n    alpha: float,\n    sigma: float,\n    same_dxdy: bool,\n    kernel_size: tuple[int, int],\n    random_generator: np.random.Generator,\n    noise_distribution: Literal[\"gaussian\", \"uniform\"],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate displacement fields for elastic transform.\n\n    Args:\n        image_shape: Shape of the image (height, width)\n        alpha: Scaling factor for displacement\n        sigma: Standard deviation for Gaussian blur\n        same_dxdy: Whether to use same displacement field for both directions\n        kernel_size: Size of Gaussian blur kernel\n        random_generator: NumPy random number generator\n        noise_distribution: Type of noise distribution to use (\"gaussian\" or \"uniform\")\n\n    Returns:\n        tuple: (dx, dy) displacement fields\n    \"\"\"\n\n    def generate_noise_field() -> np.ndarray:\n        # Generate noise based on distribution type\n        if noise_distribution == \"gaussian\":\n            field = random_generator.standard_normal(size=image_shape[:2])\n        else:  # uniform\n            field = random_generator.uniform(low=-1, high=1, size=image_shape[:2])\n\n        # Common operations for both distributions\n        field = field.astype(np.float32)\n        cv2.GaussianBlur(field, kernel_size, sigma, dst=field)\n        return field * alpha\n\n    # Generate first displacement field\n    dx = generate_noise_field()\n\n    # Generate or copy second displacement field\n    dy = dx if same_dxdy else generate_noise_field()\n\n    return dx, dy\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_distorted_grid_polygons","title":"def generate_distorted_grid_polygons (dimensions, magnitude, random_generator) [view source on GitHub]","text":"

Generate distorted grid polygons based on input dimensions and magnitude.

This function creates a grid of polygons and applies random distortions to the internal vertices, while keeping the boundary vertices fixed. The distortion is applied consistently across shared vertices to avoid gaps or overlaps in the resulting grid.

Parameters:

Name Type Description dimensions np.ndarray

A 3D array of shape (grid_height, grid_width, 4) where each element is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.

magnitude int

Maximum pixel-wise displacement for distortion. The actual displacement will be randomly chosen in the range [-magnitude, magnitude].

random_generator np.random.Generator

A random number generator.

Returns:

Type Description np.ndarray

A 2D array of shape (total_cells, 8) where each row represents a distorted polygon as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.

Note

  • Only internal grid points are distorted; boundary points remain fixed.
  • The function ensures consistent distortion across shared vertices of adjacent cells.
  • The distortion is applied to the following points of each internal cell:
    • Bottom-right of the cell above and to the left
    • Bottom-left of the cell above
    • Top-right of the cell to the left
    • Top-left of the current cell
  • Each square represents a cell, and the X marks indicate the coordinates where displacement occurs. +--+--+--+--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--X--X--X--+ | | | | | +--+--+--+--+
  • For each X, the coordinates of the left, right, top, and bottom edges in the four adjacent cells are displaced.

Examples:

Python
>>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n>>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n>>> distorted.shape\n(4, 8)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_distorted_grid_polygons(\n    dimensions: np.ndarray,\n    magnitude: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate distorted grid polygons based on input dimensions and magnitude.\n\n    This function creates a grid of polygons and applies random distortions to the internal vertices,\n    while keeping the boundary vertices fixed. The distortion is applied consistently across shared\n    vertices to avoid gaps or overlaps in the resulting grid.\n\n    Args:\n        dimensions (np.ndarray): A 3D array of shape (grid_height, grid_width, 4) where each element\n                                 is [x_min, y_min, x_max, y_max] representing the dimensions of a grid cell.\n        magnitude (int): Maximum pixel-wise displacement for distortion. The actual displacement\n                         will be randomly chosen in the range [-magnitude, magnitude].\n        random_generator (np.random.Generator): A random number generator.\n\n    Returns:\n        np.ndarray: A 2D array of shape (total_cells, 8) where each row represents a distorted polygon\n                    as [x1, y1, x2, y1, x2, y2, x1, y2]. The total_cells is equal to grid_height * grid_width.\n\n    Note:\n        - Only internal grid points are distorted; boundary points remain fixed.\n        - The function ensures consistent distortion across shared vertices of adjacent cells.\n        - The distortion is applied to the following points of each internal cell:\n            * Bottom-right of the cell above and to the left\n            * Bottom-left of the cell above\n            * Top-right of the cell to the left\n            * Top-left of the current cell\n        - Each square represents a cell, and the X marks indicate the coordinates where displacement occurs.\n            +--+--+--+--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--X--X--X--+\n            |  |  |  |  |\n            +--+--+--+--+\n        - For each X, the coordinates of the left, right, top, and bottom edges\n          in the four adjacent cells are displaced.\n\n    Example:\n        >>> dimensions = np.array([[[0, 0, 50, 50], [50, 0, 100, 50]],\n        ...                        [[0, 50, 50, 100], [50, 50, 100, 100]]])\n        >>> distorted = generate_distorted_grid_polygons(dimensions, magnitude=10)\n        >>> distorted.shape\n        (4, 8)\n    \"\"\"\n    grid_height, grid_width = dimensions.shape[:2]\n    total_cells = grid_height * grid_width\n\n    # Initialize polygons\n    polygons = np.zeros((total_cells, 8), dtype=np.float32)\n    polygons[:, 0:2] = dimensions.reshape(-1, 4)[:, [0, 1]]  # x1, y1\n    polygons[:, 2:4] = dimensions.reshape(-1, 4)[:, [2, 1]]  # x2, y1\n    polygons[:, 4:6] = dimensions.reshape(-1, 4)[:, [2, 3]]  # x2, y2\n    polygons[:, 6:8] = dimensions.reshape(-1, 4)[:, [0, 3]]  # x1, y2\n\n    # Generate displacements for internal grid points only\n    internal_points_height, internal_points_width = grid_height - 1, grid_width - 1\n    displacements = random_generator.integers(\n        -magnitude,\n        magnitude + 1,\n        size=(internal_points_height, internal_points_width, 2),\n    ).astype(np.float32)\n\n    # Apply displacements to internal polygon vertices\n    for i in range(1, grid_height):\n        for j in range(1, grid_width):\n            dx, dy = displacements[i - 1, j - 1]\n\n            # Bottom-right of cell (i-1, j-1)\n            polygons[(i - 1) * grid_width + (j - 1), 4:6] += [dx, dy]\n\n            # Bottom-left of cell (i-1, j)\n            polygons[(i - 1) * grid_width + j, 6:8] += [dx, dy]\n\n            # Top-right of cell (i, j-1)\n            polygons[i * grid_width + (j - 1), 2:4] += [dx, dy]\n\n            # Top-left of cell (i, j)\n            polygons[i * grid_width + j, 0:2] += [dx, dy]\n\n    return polygons\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_grid","title":"def generate_grid (image_shape, steps_x, steps_y, num_steps) [view source on GitHub]","text":"

Generate a distorted grid for image transformation based on given step sizes.

This function creates two 2D arrays (map_x and map_y) that represent a distorted version of the original image grid. These arrays can be used with OpenCV's remap function to apply grid distortion to an image.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

steps_x list[float]

List of step sizes for the x-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the x direction.

steps_y list[float]

List of step sizes for the y-axis distortion. The length should be num_steps + 1. Each value represents the relative step size for a segment of the grid in the y direction.

num_steps int

The number of steps to divide each axis into. This determines the granularity of the distortion grid.

Returns:

Type Description tuple[np.ndarray, np.ndarray]

A tuple containing two 2D numpy arrays: - map_x: A 2D array of float32 values representing the x-coordinates of the distorted grid. - map_y: A 2D array of float32 values representing the y-coordinates of the distorted grid.

Note

  • The function generates a grid where each cell can be distorted independently.
  • The distortion is controlled by the steps_x and steps_y parameters, which determine how much each grid line is shifted.
  • The resulting map_x and map_y can be used directly with cv2.remap() to apply the distortion to an image.
  • The distortion is applied smoothly across each grid cell using linear interpolation.

Examples:

Python
>>> image_shape = (100, 100)\n>>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n>>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n>>> num_steps = 5\n>>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n>>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_grid(\n    image_shape: tuple[int, int],\n    steps_x: list[float],\n    steps_y: list[float],\n    num_steps: int,\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate a distorted grid for image transformation based on given step sizes.\n\n    This function creates two 2D arrays (map_x and map_y) that represent a distorted version\n    of the original image grid. These arrays can be used with OpenCV's remap function to\n    apply grid distortion to an image.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        steps_x (list[float]): List of step sizes for the x-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the x direction.\n        steps_y (list[float]): List of step sizes for the y-axis distortion. The length\n            should be num_steps + 1. Each value represents the relative step size for\n            a segment of the grid in the y direction.\n        num_steps (int): The number of steps to divide each axis into. This determines\n            the granularity of the distortion grid.\n\n    Returns:\n        tuple[np.ndarray, np.ndarray]: A tuple containing two 2D numpy arrays:\n            - map_x: A 2D array of float32 values representing the x-coordinates\n              of the distorted grid.\n            - map_y: A 2D array of float32 values representing the y-coordinates\n              of the distorted grid.\n\n    Note:\n        - The function generates a grid where each cell can be distorted independently.\n        - The distortion is controlled by the steps_x and steps_y parameters, which\n          determine how much each grid line is shifted.\n        - The resulting map_x and map_y can be used directly with cv2.remap() to\n          apply the distortion to an image.\n        - The distortion is applied smoothly across each grid cell using linear\n          interpolation.\n\n    Example:\n        >>> image_shape = (100, 100)\n        >>> steps_x = [1.1, 0.9, 1.0, 1.2, 0.95, 1.05]\n        >>> steps_y = [0.9, 1.1, 1.0, 1.1, 0.9, 1.0]\n        >>> num_steps = 5\n        >>> map_x, map_y = generate_grid(image_shape, steps_x, steps_y, num_steps)\n        >>> distorted_image = cv2.remap(image, map_x, map_y, cv2.INTER_LINEAR)\n    \"\"\"\n    height, width = image_shape[:2]\n    x_step = width // num_steps\n    xx = np.zeros(width, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_x):\n        x = idx * x_step\n        start = int(x)\n        end = min(int(x) + x_step, width)\n        cur = prev + x_step * step\n        xx[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    y_step = height // num_steps\n    yy = np.zeros(height, np.float32)\n    prev = 0.0\n    for idx, step in enumerate(steps_y):\n        y = idx * y_step\n        start = int(y)\n        end = min(int(y) + y_step, height)\n        cur = prev + y_step * step\n        yy[start:end] = np.linspace(prev, cur, end - start)\n        prev = cur\n\n    return np.meshgrid(xx, yy)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_reflected_bboxes","title":"def generate_reflected_bboxes (bboxes, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected bounding boxes for the entire reflection grid.

Parameters:

Name Type Description bboxes np.ndarray

Original bounding boxes.

grid_dims dict[str, tuple[int, int]]

Grid dimensions and original position.

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted bounding boxes for the entire grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_bboxes(\n    bboxes: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected bounding boxes for the entire reflection grid.\n\n    Args:\n        bboxes (np.ndarray): Original bounding boxes.\n        grid_dims (dict[str, tuple[int, int]]): Grid dimensions and original position.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted bounding boxes for the entire grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of bboxes\n    bboxes_hflipped = flip_bboxes(bboxes, flip_horizontal=True, image_shape=image_shape)\n    bboxes_vflipped = flip_bboxes(bboxes, flip_vertical=True, image_shape=image_shape)\n    bboxes_hvflipped = flip_bboxes(\n        bboxes,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [\n            original_col * cols,\n            original_row * rows,\n            original_col * cols,\n            original_row * rows,\n        ],\n    )\n    bboxes = shift_bboxes(bboxes, shift_vector)\n    bboxes_hflipped = shift_bboxes(bboxes_hflipped, shift_vector)\n    bboxes_vflipped = shift_bboxes(bboxes_vflipped, shift_vector)\n    bboxes_hvflipped = shift_bboxes(bboxes_hvflipped, shift_vector)\n\n    new_bboxes = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of bboxes to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes\n            elif (grid_row - original_row) % 2 == 0:\n                current_bboxes = bboxes_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_bboxes = bboxes_vflipped\n            else:\n                current_bboxes = bboxes_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                ],\n            )\n            shifted_bboxes = shift_bboxes(current_bboxes, cell_shift)\n\n            new_bboxes.append(shifted_bboxes)\n\n    result = np.vstack(new_bboxes)\n\n    return shift_bboxes(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_reflected_keypoints","title":"def generate_reflected_keypoints (keypoints, grid_dims, image_shape, center_in_origin=False) [view source on GitHub]","text":"

Generate reflected keypoints for the entire reflection grid.

This function creates a grid of keypoints by reflecting and shifting the original keypoints. It handles both centered and non-centered grids based on the center_in_origin parameter.

Parameters:

Name Type Description keypoints np.ndarray

Original keypoints array of shape (N, 4+), where N is the number of keypoints, and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).

grid_dims dict[str, tuple[int, int]]

A dictionary containing grid dimensions and original position. It should have the following keys: - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols) - \"original_position\": tuple[int, int] representing (original_row, original_col)

image_shape tuple[int, int]

Shape of the original image as (height, width).

center_in_origin bool

If True, center the grid at the origin. Default is False.

Returns:

Type Description np.ndarray

Array of reflected and shifted keypoints for the entire grid. The shape is (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.

Note

  • The function handles keypoint flipping and shifting to create a grid of reflected keypoints.
  • It preserves the angle and scale information of the keypoints during transformations.
  • The resulting grid can be either centered at the origin or positioned based on the original grid.
Source code in albumentations/augmentations/geometric/functional.py Python
def generate_reflected_keypoints(\n    keypoints: np.ndarray,\n    grid_dims: dict[str, tuple[int, int]],\n    image_shape: tuple[int, int],\n    center_in_origin: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate reflected keypoints for the entire reflection grid.\n\n    This function creates a grid of keypoints by reflecting and shifting the original keypoints.\n    It handles both centered and non-centered grids based on the `center_in_origin` parameter.\n\n    Args:\n        keypoints (np.ndarray): Original keypoints array of shape (N, 4+), where N is the number of keypoints,\n                                and each keypoint is represented by at least 4 values (x, y, angle, scale, ...).\n        grid_dims (dict[str, tuple[int, int]]): A dictionary containing grid dimensions and original position.\n            It should have the following keys:\n            - \"grid_shape\": tuple[int, int] representing (grid_rows, grid_cols)\n            - \"original_position\": tuple[int, int] representing (original_row, original_col)\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n        center_in_origin (bool, optional): If True, center the grid at the origin. Default is False.\n\n    Returns:\n        np.ndarray: Array of reflected and shifted keypoints for the entire grid. The shape is\n                    (N * grid_rows * grid_cols, 4+), where N is the number of original keypoints.\n\n    Note:\n        - The function handles keypoint flipping and shifting to create a grid of reflected keypoints.\n        - It preserves the angle and scale information of the keypoints during transformations.\n        - The resulting grid can be either centered at the origin or positioned based on the original grid.\n    \"\"\"\n    grid_rows, grid_cols = grid_dims[\"grid_shape\"]\n    original_row, original_col = grid_dims[\"original_position\"]\n\n    # Prepare flipped versions of keypoints\n    keypoints_hflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        image_shape=image_shape,\n    )\n    keypoints_vflipped = flip_keypoints(\n        keypoints,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n    keypoints_hvflipped = flip_keypoints(\n        keypoints,\n        flip_horizontal=True,\n        flip_vertical=True,\n        image_shape=image_shape,\n    )\n\n    rows, cols = image_shape[:2]\n\n    # Shift all versions to the original position\n    shift_vector = np.array(\n        [original_col * cols, original_row * rows, 0, 0, 0],\n    )  # Only shift x and y\n    keypoints = shift_keypoints(keypoints, shift_vector)\n    keypoints_hflipped = shift_keypoints(keypoints_hflipped, shift_vector)\n    keypoints_vflipped = shift_keypoints(keypoints_vflipped, shift_vector)\n    keypoints_hvflipped = shift_keypoints(keypoints_hvflipped, shift_vector)\n\n    new_keypoints = []\n\n    for grid_row in range(grid_rows):\n        for grid_col in range(grid_cols):\n            # Determine which version of keypoints to use based on grid position\n            if (grid_row - original_row) % 2 == 0 and (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints\n            elif (grid_row - original_row) % 2 == 0:\n                current_keypoints = keypoints_hflipped\n            elif (grid_col - original_col) % 2 == 0:\n                current_keypoints = keypoints_vflipped\n            else:\n                current_keypoints = keypoints_hvflipped\n\n            # Shift to the current grid cell\n            cell_shift = np.array(\n                [\n                    (grid_col - original_col) * cols,\n                    (grid_row - original_row) * rows,\n                    0,\n                    0,\n                    0,\n                ],\n            )\n            shifted_keypoints = shift_keypoints(current_keypoints, cell_shift)\n\n            new_keypoints.append(shifted_keypoints)\n\n    result = np.vstack(new_keypoints)\n\n    return shift_keypoints(result, -shift_vector) if center_in_origin else result\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.generate_shuffled_splits","title":"def generate_shuffled_splits (size, divisions, random_generator) [view source on GitHub]","text":"

Generate shuffled splits for a given dimension size and number of divisions.

Parameters:

Name Type Description size int

Total size of the dimension (height or width).

divisions int

Number of divisions (rows or columns).

random_generator np.random.Generator | None

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

Cumulative edges of the shuffled intervals.

Source code in albumentations/augmentations/geometric/functional.py Python
def generate_shuffled_splits(\n    size: int,\n    divisions: int,\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Generate shuffled splits for a given dimension size and number of divisions.\n\n    Args:\n        size (int): Total size of the dimension (height or width).\n        divisions (int): Number of divisions (rows or columns).\n        random_generator (np.random.Generator | None): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: Cumulative edges of the shuffled intervals.\n    \"\"\"\n    intervals = almost_equal_intervals(size, divisions)\n    random_generator.shuffle(intervals)\n    return np.insert(np.cumsum(intervals), 0, 0)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_camera_matrix_distortion_maps","title":"def get_camera_matrix_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using camera matrix model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_camera_matrix_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using camera matrix model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n    camera_matrix = np.array(\n        [[width, 0, center_xy[0]], [0, height, center_xy[1]], [0, 0, 1]],\n        dtype=np.float32,\n    )\n    distortion = np.array([k, k, 0, 0, 0], dtype=np.float32)\n    return cv2.initUndistortRectifyMap(\n        camera_matrix,\n        distortion,\n        None,\n        None,\n        (width, height),\n        cv2.CV_32FC1,\n    )\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_dimension_padding","title":"def get_dimension_padding (current_size, min_size, divisor) [view source on GitHub]","text":"

Calculate padding for a single dimension.

Parameters:

Name Type Description current_size int

Current size of the dimension

min_size int | None

Minimum size requirement, if any

divisor int | None

Divisor for padding to make size divisible, if any

Returns:

Type Description tuple[int, int]

(pad_before, pad_after)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_dimension_padding(\n    current_size: int,\n    min_size: int | None,\n    divisor: int | None,\n) -> tuple[int, int]:\n    \"\"\"Calculate padding for a single dimension.\n\n    Args:\n        current_size: Current size of the dimension\n        min_size: Minimum size requirement, if any\n        divisor: Divisor for padding to make size divisible, if any\n\n    Returns:\n        tuple[int, int]: (pad_before, pad_after)\n    \"\"\"\n    if min_size is not None:\n        if current_size < min_size:\n            pad_before = int((min_size - current_size) / 2.0)\n            pad_after = min_size - current_size - pad_before\n            return pad_before, pad_after\n    elif divisor is not None:\n        remainder = current_size % divisor\n        if remainder > 0:\n            total_pad = divisor - remainder\n            pad_before = total_pad // 2\n            pad_after = total_pad - pad_before\n            return pad_before, pad_after\n\n    return 0, 0\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_fisheye_distortion_maps","title":"def get_fisheye_distortion_maps (image_shape, k, center_xy) [view source on GitHub]","text":"

Generate distortion maps using fisheye model.

Parameters:

Name Type Description image_shape tuple[int, int]

Image shape

k float

Distortion coefficient

center_xy tuple[float, float]

Center of distortion

Returns:

Type Description tuple of
  • map_x: Horizontal displacement map
  • map_y: Vertical displacement map
Source code in albumentations/augmentations/geometric/functional.py Python
def get_fisheye_distortion_maps(\n    image_shape: tuple[int, int],\n    k: float,\n    center_xy: tuple[float, float],\n) -> tuple[np.ndarray, np.ndarray]:\n    \"\"\"Generate distortion maps using fisheye model.\n\n    Args:\n        image_shape: Image shape\n        k: Distortion coefficient\n        center_xy: Center of distortion\n    Returns:\n        tuple of:\n        - map_x: Horizontal displacement map\n        - map_y: Vertical displacement map\n    \"\"\"\n    height, width = image_shape[:2]\n\n    center_x, center_y = center_xy\n\n    # Create coordinate grid\n    y, x = np.mgrid[:height, :width].astype(np.float32)\n\n    x = x - center_x\n    y = y - center_y\n\n    # Calculate polar coordinates\n    r = np.sqrt(x * x + y * y)\n    theta = np.arctan2(y, x)\n\n    # Normalize radius by the maximum possible radius to keep distortion in check\n    max_radius = math.sqrt(max(center_x, width - center_x) ** 2 + max(center_y, height - center_y) ** 2)\n    r_norm = r / max_radius\n\n    # Apply fisheye distortion to normalized radius\n    r_dist = r * (1 + k * r_norm * r_norm)\n\n    # Convert back to cartesian coordinates\n    map_x = r_dist * np.cos(theta) + center_x\n    map_y = r_dist * np.sin(theta) + center_y\n\n    return map_x, map_y\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_pad_grid_dimensions","title":"def get_pad_grid_dimensions (pad_top, pad_bottom, pad_left, pad_right, image_shape) [view source on GitHub]","text":"

Calculate the dimensions of the grid needed for reflection padding and the position of the original image.

Parameters:

Name Type Description pad_top int

Number of pixels to pad above the image.

pad_bottom int

Number of pixels to pad below the image.

pad_left int

Number of pixels to pad to the left of the image.

pad_right int

Number of pixels to pad to the right of the image.

image_shape tuple[int, int]

Shape of the original image as (height, width).

Returns:

Type Description dict[str, tuple[int, int]]

A dictionary containing: - 'grid_shape': A tuple (grid_rows, grid_cols) where: - grid_rows (int): Number of times the image needs to be repeated vertically. - grid_cols (int): Number of times the image needs to be repeated horizontally. - 'original_position': A tuple (original_row, original_col) where: - original_row (int): Row index of the original image in the grid. - original_col (int): Column index of the original image in the grid.

Source code in albumentations/augmentations/geometric/functional.py Python
def get_pad_grid_dimensions(\n    pad_top: int,\n    pad_bottom: int,\n    pad_left: int,\n    pad_right: int,\n    image_shape: tuple[int, int],\n) -> dict[str, tuple[int, int]]:\n    \"\"\"Calculate the dimensions of the grid needed for reflection padding and the position of the original image.\n\n    Args:\n        pad_top (int): Number of pixels to pad above the image.\n        pad_bottom (int): Number of pixels to pad below the image.\n        pad_left (int): Number of pixels to pad to the left of the image.\n        pad_right (int): Number of pixels to pad to the right of the image.\n        image_shape (tuple[int, int]): Shape of the original image as (height, width).\n\n    Returns:\n        dict[str, tuple[int, int]]: A dictionary containing:\n            - 'grid_shape': A tuple (grid_rows, grid_cols) where:\n                - grid_rows (int): Number of times the image needs to be repeated vertically.\n                - grid_cols (int): Number of times the image needs to be repeated horizontally.\n            - 'original_position': A tuple (original_row, original_col) where:\n                - original_row (int): Row index of the original image in the grid.\n                - original_col (int): Column index of the original image in the grid.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    grid_rows = 1 + math.ceil(pad_top / rows) + math.ceil(pad_bottom / rows)\n    grid_cols = 1 + math.ceil(pad_left / cols) + math.ceil(pad_right / cols)\n    original_row = math.ceil(pad_top / rows)\n    original_col = math.ceil(pad_left / cols)\n\n    return {\n        \"grid_shape\": (grid_rows, grid_cols),\n        \"original_position\": (original_row, original_col),\n    }\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.get_padding_params","title":"def get_padding_params (image_shape, min_height, min_width, pad_height_divisor, pad_width_divisor) [view source on GitHub]","text":"

Calculate padding parameters based on target dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

(height, width) of the image

min_height int | None

Minimum height requirement, if any

min_width int | None

Minimum width requirement, if any

pad_height_divisor int | None

Divisor for height padding, if any

pad_width_divisor int | None

Divisor for width padding, if any

Returns:

Type Description tuple[int, int, int, int]

(pad_top, pad_bottom, pad_left, pad_right)

Source code in albumentations/augmentations/geometric/functional.py Python
def get_padding_params(\n    image_shape: tuple[int, int],\n    min_height: int | None,\n    min_width: int | None,\n    pad_height_divisor: int | None,\n    pad_width_divisor: int | None,\n) -> tuple[int, int, int, int]:\n    \"\"\"Calculate padding parameters based on target dimensions.\n\n    Args:\n        image_shape: (height, width) of the image\n        min_height: Minimum height requirement, if any\n        min_width: Minimum width requirement, if any\n        pad_height_divisor: Divisor for height padding, if any\n        pad_width_divisor: Divisor for width padding, if any\n\n    Returns:\n        tuple[int, int, int, int]: (pad_top, pad_bottom, pad_left, pad_right)\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    h_pad_top, h_pad_bottom = get_dimension_padding(\n        rows,\n        min_height,\n        pad_height_divisor,\n    )\n    w_pad_left, w_pad_right = get_dimension_padding(cols, min_width, pad_width_divisor)\n\n    return h_pad_top, h_pad_bottom, w_pad_left, w_pad_right\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.is_identity_matrix","title":"def is_identity_matrix (matrix) [view source on GitHub]","text":"

Check if the given matrix is an identity matrix.

Parameters:

Name Type Description matrix np.ndarray

A 3x3 affine transformation matrix.

Returns:

Type Description bool

True if the matrix is an identity matrix, False otherwise.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_identity_matrix(matrix: np.ndarray) -> bool:\n    \"\"\"Check if the given matrix is an identity matrix.\n\n    Args:\n        matrix (np.ndarray): A 3x3 affine transformation matrix.\n\n    Returns:\n        bool: True if the matrix is an identity matrix, False otherwise.\n    \"\"\"\n    return np.allclose(matrix, np.eye(3, dtype=matrix.dtype))\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.is_valid_component","title":"def is_valid_component (component_area, original_area, min_area, min_visibility) [view source on GitHub]","text":"

Validate if a component meets the minimum requirements.

Source code in albumentations/augmentations/geometric/functional.py Python
def is_valid_component(\n    component_area: float,\n    original_area: float,\n    min_area: float | None,\n    min_visibility: float | None,\n) -> bool:\n    \"\"\"Validate if a component meets the minimum requirements.\"\"\"\n    visibility = component_area / original_area\n    return (min_area is None or component_area >= min_area) and (min_visibility is None or visibility >= min_visibility)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_affine","title":"def keypoints_affine (keypoints, matrix, image_shape, scale, border_mode) [view source on GitHub]","text":"

Apply an affine transformation to keypoints.

This function transforms keypoints using the given affine transformation matrix. It handles reflection padding if necessary, updates coordinates, angles, and scales.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 4+) where N is the number of keypoints. Each keypoint is represented as [x, y, angle, scale, ...].

matrix np.ndarray

The 2x3 or 3x3 affine transformation matrix.

image_shape tuple[int, int]

Shape of the image (height, width).

scale dict[str, float]

Dictionary containing scale factors for x and y directions. Expected keys are 'x' and 'y'.

border_mode int

Border mode for handling keypoints near image edges. Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.

Returns:

Type Description np.ndarray

Transformed keypoints array with the same shape as input.

Notes

  • The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.
  • Coordinates (x, y) are transformed using the affine matrix.
  • Angles are adjusted based on the rotation component of the affine transformation.
  • Scales are multiplied by the maximum of x and y scale factors.
  • The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.

Examples:

Python
>>> keypoints = np.array([[100, 100, 0, 1]])\n>>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n>>> scale = {'x': 1.5, 'y': 1.2}\n>>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_affine(\n    keypoints: np.ndarray,\n    matrix: np.ndarray,\n    image_shape: tuple[int, int],\n    scale: XYFloat,\n    border_mode: int,\n) -> np.ndarray:\n    \"\"\"Apply an affine transformation to keypoints.\n\n    This function transforms keypoints using the given affine transformation matrix.\n    It handles reflection padding if necessary, updates coordinates, angles, and scales.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 4+) where N is the number of keypoints.\n                                Each keypoint is represented as [x, y, angle, scale, ...].\n        matrix (np.ndarray): The 2x3 or 3x3 affine transformation matrix.\n        image_shape (tuple[int, int]): Shape of the image (height, width).\n        scale (dict[str, float]): Dictionary containing scale factors for x and y directions.\n                                  Expected keys are 'x' and 'y'.\n        border_mode (int): Border mode for handling keypoints near image edges.\n                            Use cv2.BORDER_REFLECT_101, cv2.BORDER_REFLECT, etc.\n\n    Returns:\n        np.ndarray: Transformed keypoints array with the same shape as input.\n\n    Notes:\n        - The function applies reflection padding if the mode is in REFLECT_BORDER_MODES.\n        - Coordinates (x, y) are transformed using the affine matrix.\n        - Angles are adjusted based on the rotation component of the affine transformation.\n        - Scales are multiplied by the maximum of x and y scale factors.\n        - The @angle_2pi_range decorator ensures angles remain in the [0, 2\u03c0] range.\n\n    Example:\n        >>> keypoints = np.array([[100, 100, 0, 1]])\n        >>> matrix = np.array([[1.5, 0, 10], [0, 1.2, 20]])\n        >>> scale = {'x': 1.5, 'y': 1.2}\n        >>> transformed_keypoints = keypoints_affine(keypoints, matrix, (480, 640), scale, cv2.BORDER_REFLECT_101)\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    if is_identity_matrix(matrix):\n        return keypoints\n\n    if border_mode in REFLECT_BORDER_MODES:\n        # Step 1: Compute affine transform padding\n        pad_left, pad_right, pad_top, pad_bottom = calculate_affine_transform_padding(\n            matrix,\n            image_shape,\n        )\n        grid_dimensions = get_pad_grid_dimensions(\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            image_shape,\n        )\n        keypoints = generate_reflected_keypoints(\n            keypoints,\n            grid_dimensions,\n            image_shape,\n            center_in_origin=True,\n        )\n\n    # Extract x, y coordinates (z is preserved)\n    xy = keypoints[:, :2]\n\n    # Ensure matrix is 2x3\n    if matrix.shape == (3, 3):\n        matrix = matrix[:2]\n\n    # Transform x, y coordinates\n    xy_transformed = cv2.transform(xy.reshape(-1, 1, 2), matrix).squeeze()\n\n    # Calculate angle adjustment\n    angle_adjustment = rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=False)\n\n    # Update angles (now at index 3)\n    keypoints[:, 3] = keypoints[:, 3] + angle_adjustment\n\n    # Update scales (now at index 4)\n    max_scale = max(scale[\"x\"], scale[\"y\"])\n    keypoints[:, 4] *= max_scale\n\n    # Update x, y coordinates and preserve z\n    keypoints[:, :2] = xy_transformed\n\n    return keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_d4","title":"def keypoints_d4 (keypoints, group_member, image_shape, ** params) [view source on GitHub]","text":"

Applies a D_4 symmetry group transformation to a keypoint.

This function adjusts a keypoint's coordinates according to the specified D_4 group transformation, which includes rotations and reflections suitable for image processing tasks. These transformations account for the dimensions of the image to ensure the keypoint remains within its boundaries.

  • keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...). -group_member (D4Type): A string identifier for the D_4 group transformation to apply. Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.
  • image_shape (tuple[int, int]): The shape of the image.
  • params (Any): Not used
  • KeypointInternalType: The transformed keypoint.
  • ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.

Examples:

  • Rotating a keypoint by 90 degrees in a 100x100 image: keypoint_d4((50, 30), 'r90', 100, 100) This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_d4(\n    keypoints: np.ndarray,\n    group_member: D4Type,\n    image_shape: tuple[int, int],\n    **params: Any,\n) -> np.ndarray:\n    \"\"\"Applies a `D_4` symmetry group transformation to a keypoint.\n\n    This function adjusts a keypoint's coordinates according to the specified `D_4` group transformation,\n    which includes rotations and reflections suitable for image processing tasks. These transformations account\n    for the dimensions of the image to ensure the keypoint remains within its boundaries.\n\n    Parameters:\n    - keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n    -group_member (D4Type): A string identifier for the `D_4` group transformation to apply.\n        Valid values are 'e', 'r90', 'r180', 'r270', 'v', 'hv', 'h', 't'.\n    - image_shape (tuple[int, int]): The shape of the image.\n    - params (Any): Not used\n\n    Returns:\n    - KeypointInternalType: The transformed keypoint.\n\n    Raises:\n    - ValueError: If an invalid group member is specified, indicating that the specified transformation does not exist.\n\n    Examples:\n    - Rotating a keypoint by 90 degrees in a 100x100 image:\n      `keypoint_d4((50, 30), 'r90', 100, 100)`\n      This would move the keypoint from (50, 30) to (70, 50) assuming standard coordinate transformations.\n    \"\"\"\n    rows, cols = image_shape[:2]\n    transformations = {\n        \"e\": lambda x: x,  # Identity transformation\n        \"r90\": lambda x: keypoints_rot90(x, 1, image_shape),  # Rotate 90 degrees\n        \"r180\": lambda x: keypoints_rot90(x, 2, image_shape),  # Rotate 180 degrees\n        \"r270\": lambda x: keypoints_rot90(x, 3, image_shape),  # Rotate 270 degrees\n        \"v\": lambda x: keypoints_vflip(x, rows),  # Vertical flip\n        \"hvt\": lambda x: keypoints_transpose(\n            keypoints_rot90(x, 2, image_shape),\n        ),  # Reflect over anti diagonal\n        \"h\": lambda x: keypoints_hflip(x, cols),  # Horizontal flip\n        \"t\": lambda x: keypoints_transpose(x),  # Transpose (reflect over main diagonal)\n    }\n    # Execute the appropriate transformation\n    if group_member in transformations:\n        return transformations[group_member](keypoints)\n\n    raise ValueError(f\"Invalid group member: {group_member}\")\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_hflip","title":"def keypoints_hflip (keypoints, cols) [view source on GitHub]","text":"

Flip keypoints horizontally around the y-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

cols int

Image width.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_hflip(keypoints: np.ndarray, cols: int) -> np.ndarray:\n    \"\"\"Flip keypoints horizontally around the y-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        cols: Image width.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip x-coordinates\n    flipped_keypoints[:, 0] = (cols - 1) - keypoints[:, 0]\n\n    # Adjust angles\n    flipped_keypoints[:, 3] = np.pi - keypoints[:, 3]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_rot90","title":"def keypoints_rot90 (keypoints, factor, image_shape) [view source on GitHub]","text":"

Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.

Parameters:

Name Type Description keypoints np.ndarray

An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).

factor int

The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].

image_shape tuple[int, int]

The shape of the image (height, width).

Returns:

Type Description np.ndarray

The rotated keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_rot90(\n    keypoints: np.ndarray,\n    factor: Literal[0, 1, 2, 3],\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Rotate keypoints by 90 degrees counter-clockwise (CCW) a specified number of times.\n\n    Args:\n        keypoints (np.ndarray): An array of keypoints with shape (N, 4+) in the format (x, y, angle, scale, ...).\n        factor (int): The number of 90 degree CCW rotations to apply. Must be in the range [0, 3].\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n\n    Returns:\n        np.ndarray: The rotated keypoints with the same shape as the input.\n    \"\"\"\n    if factor == 0:\n        return keypoints\n\n    height, width = image_shape[:2]\n    rotated_keypoints = keypoints.copy().astype(np.float32)\n\n    x, y, angle = keypoints[:, 0], keypoints[:, 1], keypoints[:, 3]\n\n    if factor == 1:\n        rotated_keypoints[:, 0] = y\n        rotated_keypoints[:, 1] = width - 1 - x\n        rotated_keypoints[:, 3] = angle - np.pi / 2\n    elif factor == ROT90_180_FACTOR:\n        rotated_keypoints[:, 0] = width - 1 - x\n        rotated_keypoints[:, 1] = height - 1 - y\n        rotated_keypoints[:, 3] = angle - np.pi\n    elif factor == ROT90_270_FACTOR:\n        rotated_keypoints[:, 0] = height - 1 - y\n        rotated_keypoints[:, 1] = x\n        rotated_keypoints[:, 3] = angle + np.pi / 2\n\n    return rotated_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_scale","title":"def keypoints_scale (keypoints, scale_x, scale_y) [view source on GitHub]","text":"

Scales keypoints by scale_x and scale_y.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of keypoints with shape (N, 5+) in the format (x, y, z, angle, scale, ...).

scale_x float

Scale coefficient x-axis.

scale_y float

Scale coefficient y-axis.

Returns:

Type Description np.ndarray

A numpy array of scaled keypoints with the same shape as input. X and Y coordinates are scaled by their respective scale factors, Z coordinate remains unchanged, and the keypoint scale is multiplied by max(scale_x, scale_y).

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\ndef keypoints_scale(\n    keypoints: np.ndarray,\n    scale_x: float,\n    scale_y: float,\n) -> np.ndarray:\n    \"\"\"Scales keypoints by scale_x and scale_y.\n\n    Args:\n        keypoints: A numpy array of keypoints with shape (N, 5+) in the format\n                  (x, y, z, angle, scale, ...).\n        scale_x: Scale coefficient x-axis.\n        scale_y: Scale coefficient y-axis.\n\n    Returns:\n        A numpy array of scaled keypoints with the same shape as input.\n        X and Y coordinates are scaled by their respective scale factors,\n        Z coordinate remains unchanged, and the keypoint scale is multiplied\n        by max(scale_x, scale_y).\n    \"\"\"\n    # Extract x, y, z, angle, and scale\n    x, y, z, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n        keypoints[:, 4],\n    )\n\n    # Scale x and y\n    x_scaled = x * scale_x\n    y_scaled = y * scale_y\n\n    # Scale the keypoint scale by the maximum of scale_x and scale_y\n    scale_scaled = scale * max(scale_x, scale_y)\n\n    # Create the output array\n    scaled_keypoints = np.column_stack([x_scaled, y_scaled, z, angle, scale_scaled])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [scaled_keypoints, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]],\n        )\n\n    return scaled_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_transpose","title":"def keypoints_transpose (keypoints) [view source on GitHub]","text":"

Transposes keypoints along the main diagonal.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

Returns:

Type Description np.ndarray

An array of transposed keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_transpose(keypoints: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes keypoints along the main diagonal.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n\n    Returns:\n        np.ndarray: An array of transposed keypoints with the same shape as the input.\n    \"\"\"\n    transposed_keypoints = keypoints.copy()\n\n    # Swap x and y coordinates\n    transposed_keypoints[:, [0, 1]] = keypoints[:, [1, 0]]\n\n    # Adjust angles to reflect the coordinate swap\n    angles = keypoints[:, 3]\n    transposed_keypoints[:, 3] = np.where(\n        angles <= np.pi,\n        np.pi / 2 - angles,\n        3 * np.pi / 2 - angles,\n    )\n\n    return transposed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.keypoints_vflip","title":"def keypoints_vflip (keypoints, rows) [view source on GitHub]","text":"

Flip keypoints vertically around the x-axis.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).

rows int

Image height.

Returns:

Type Description np.ndarray

An array of flipped keypoints with the same shape as the input.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef keypoints_vflip(keypoints: np.ndarray, rows: int) -> np.ndarray:\n    \"\"\"Flip keypoints vertically around the x-axis.\n\n    Args:\n        keypoints: A numpy array of shape (N, 4+) where each row represents a keypoint (x, y, angle, scale, ...).\n        rows: Image height.\n\n    Returns:\n        np.ndarray: An array of flipped keypoints with the same shape as the input.\n    \"\"\"\n    flipped_keypoints = keypoints.copy().astype(np.float32)\n\n    # Flip y-coordinates\n    flipped_keypoints[:, 1] = (rows - 1) - keypoints[:, 1]\n\n    # Negate angles\n    flipped_keypoints[:, 3] = -keypoints[:, 3]\n\n    return flipped_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.perspective_bboxes","title":"def perspective_bboxes (bboxes, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Applies perspective transformation to bounding boxes.

This function transforms bounding boxes using the given perspective transformation matrix. It handles bounding boxes with additional attributes beyond the standard coordinates.

Parameters:

Name Type Description bboxes np.ndarray

An array of bounding boxes with shape (num_bboxes, 4+). Each row represents a bounding box (x_min, y_min, x_max, y_max, ...). Additional columns beyond the first 4 are preserved unchanged.

image_shape tuple[int, int]

The shape of the image (height, width).

matrix np.ndarray

The perspective transformation matrix.

max_width int

The maximum width of the output image.

max_height int

The maximum height of the output image.

keep_size bool

If True, maintains the original image size after transformation.

Returns:

Type Description np.ndarray

An array of transformed bounding boxes with the same shape as input. The first 4 columns contain the transformed coordinates, and any additional columns are preserved from the input.

Note

  • This function modifies only the coordinate columns (first 4) of the input bounding boxes.
  • Any additional attributes (columns beyond the first 4) are kept unchanged.
  • The function handles denormalization and renormalization of coordinates internally.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n>>> image_shape = (100, 100)\n>>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n>>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n
Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"bboxes\")\ndef perspective_bboxes(\n    bboxes: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Applies perspective transformation to bounding boxes.\n\n    This function transforms bounding boxes using the given perspective transformation matrix.\n    It handles bounding boxes with additional attributes beyond the standard coordinates.\n\n    Args:\n        bboxes (np.ndarray): An array of bounding boxes with shape (num_bboxes, 4+).\n                             Each row represents a bounding box (x_min, y_min, x_max, y_max, ...).\n                             Additional columns beyond the first 4 are preserved unchanged.\n        image_shape (tuple[int, int]): The shape of the image (height, width).\n        matrix (np.ndarray): The perspective transformation matrix.\n        max_width (int): The maximum width of the output image.\n        max_height (int): The maximum height of the output image.\n        keep_size (bool): If True, maintains the original image size after transformation.\n\n    Returns:\n        np.ndarray: An array of transformed bounding boxes with the same shape as input.\n                    The first 4 columns contain the transformed coordinates, and any\n                    additional columns are preserved from the input.\n\n    Note:\n        - This function modifies only the coordinate columns (first 4) of the input bounding boxes.\n        - Any additional attributes (columns beyond the first 4) are kept unchanged.\n        - The function handles denormalization and renormalization of coordinates internally.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.3, 0.3, 1], [0.5, 0.5, 0.8, 0.8, 2]])\n        >>> image_shape = (100, 100)\n        >>> matrix = np.array([[1.5, 0.2, -20], [-0.1, 1.3, -10], [0.002, 0.001, 1]])\n        >>> transformed_bboxes = perspective_bboxes(bboxes, image_shape, matrix, 150, 150, False)\n    \"\"\"\n    height, width = image_shape[:2]\n    transformed_bboxes = bboxes.copy()\n    denormalized_coords = denormalize_bboxes(bboxes[:, :4], image_shape)\n\n    x_min, y_min, x_max, y_max = denormalized_coords.T\n    points = np.array(\n        [[x_min, y_min], [x_max, y_min], [x_max, y_max], [x_min, y_max]],\n    ).transpose(2, 0, 1)\n    points_reshaped = points.reshape(-1, 1, 2)\n\n    transformed_points = cv2.perspectiveTransform(\n        points_reshaped.astype(np.float32),\n        matrix,\n    )\n    transformed_points = transformed_points.reshape(-1, 4, 2)\n\n    new_coords = np.array(\n        [[np.min(box[:, 0]), np.min(box[:, 1]), np.max(box[:, 0]), np.max(box[:, 1])] for box in transformed_points],\n    )\n\n    if keep_size:\n        scale_x, scale_y = width / max_width, height / max_height\n        new_coords[:, [0, 2]] *= scale_x\n        new_coords[:, [1, 3]] *= scale_y\n        output_shape = image_shape\n    else:\n        output_shape = (max_height, max_width)\n\n    normalized_coords = normalize_bboxes(new_coords, output_shape)\n    transformed_bboxes[:, :4] = normalized_coords\n\n    return transformed_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.perspective_keypoints","title":"def perspective_keypoints (keypoints, image_shape, matrix, max_width, max_height, keep_size) [view source on GitHub]","text":"

Apply perspective transformation to keypoints.

Parameters:

Name Type Description keypoints np.ndarray

Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].

image_shape tuple[int, int]

Original image shape (height, width).

matrix np.ndarray

3x3 perspective transformation matrix.

max_width int

Maximum width after transformation.

max_height int

Maximum height after transformation.

keep_size bool

Whether to keep original size.

Returns:

Type Description np.ndarray

Transformed keypoints array with same shape as input. Z coordinate remains unchanged through the transformation.

Source code in albumentations/augmentations/geometric/functional.py Python
@handle_empty_array(\"keypoints\")\n@angle_2pi_range\ndef perspective_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    matrix: np.ndarray,\n    max_width: int,\n    max_height: int,\n    keep_size: bool,\n) -> np.ndarray:\n    \"\"\"Apply perspective transformation to keypoints.\n\n    Args:\n        keypoints: Array of shape (N, 5+) in format [x, y, z, angle, scale, ...].\n        image_shape: Original image shape (height, width).\n        matrix: 3x3 perspective transformation matrix.\n        max_width: Maximum width after transformation.\n        max_height: Maximum height after transformation.\n        keep_size: Whether to keep original size.\n\n    Returns:\n        Transformed keypoints array with same shape as input.\n        Z coordinate remains unchanged through the transformation.\n    \"\"\"\n    keypoints = keypoints.copy().astype(np.float32)\n\n    height, width = image_shape[:2]\n\n    x, y, z, angle, scale = (\n        keypoints[:, 0],\n        keypoints[:, 1],\n        keypoints[:, 2],\n        keypoints[:, 3],\n        keypoints[:, 4],\n    )\n\n    # Reshape keypoints for perspective transform\n    keypoint_vector = np.column_stack((x, y)).astype(np.float32).reshape(-1, 1, 2)\n\n    # Apply perspective transform\n    transformed_points = cv2.perspectiveTransform(keypoint_vector, matrix).squeeze()\n\n    # Unsqueeze if we have a single keypoint\n    if transformed_points.ndim == 1:\n        transformed_points = transformed_points[np.newaxis, :]\n\n    x, y = transformed_points[:, 0], transformed_points[:, 1]\n\n    # Update angles\n    angle += rotation2d_matrix_to_euler_angles(matrix[:2, :2], y_up=True)\n\n    # Calculate scale factors\n    scale_x = np.sign(matrix[0, 0]) * np.sqrt(matrix[0, 0] ** 2 + matrix[0, 1] ** 2)\n    scale_y = np.sign(matrix[1, 1]) * np.sqrt(matrix[1, 0] ** 2 + matrix[1, 1] ** 2)\n    scale *= max(scale_x, scale_y)\n\n    if keep_size:\n        scale_x = width / max_width\n        scale_y = height / max_height\n        x *= scale_x\n        y *= scale_y\n        scale *= max(scale_x, scale_y)\n\n    # Create the output array with unchanged z coordinate\n    transformed_keypoints = np.column_stack([x, y, z, angle, scale])\n\n    # If there are additional columns, preserve them\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack(\n            [\n                transformed_keypoints,\n                keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:],\n            ],\n        )\n\n    return transformed_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.rotation2d_matrix_to_euler_angles","title":"def rotation2d_matrix_to_euler_angles (matrix, y_up) [view source on GitHub]","text":"

matrix (np.ndarray): Rotation matrix y_up (bool): is Y axis looks up or down

Source code in albumentations/augmentations/geometric/functional.py Python
def rotation2d_matrix_to_euler_angles(matrix: np.ndarray, y_up: bool) -> float:\n    \"\"\"Args:\n    matrix (np.ndarray): Rotation matrix\n    y_up (bool): is Y axis looks up or down\n\n    \"\"\"\n    if y_up:\n        return np.arctan2(matrix[1, 0], matrix[0, 0])\n    return np.arctan2(-matrix[1, 0], matrix[0, 0])\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.shift_bboxes","title":"def shift_bboxes (bboxes, shift_vector) [view source on GitHub]","text":"

Shift bounding boxes by a given vector.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, m) where n is the number of bboxes and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].

shift_vector np.ndarray

Vector to shift the bounding boxes by, with shape (4,) for [shift_x, shift_y, shift_x, shift_y].

Returns:

Type Description np.ndarray

Shifted bounding boxes with the same shape as input.

Source code in albumentations/augmentations/geometric/functional.py Python
def shift_bboxes(bboxes: np.ndarray, shift_vector: np.ndarray) -> np.ndarray:\n    \"\"\"Shift bounding boxes by a given vector.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, m) where n is the number of bboxes\n                             and m >= 4. The first 4 columns are [x_min, y_min, x_max, y_max].\n        shift_vector (np.ndarray): Vector to shift the bounding boxes by, with shape (4,) for\n                                   [shift_x, shift_y, shift_x, shift_y].\n\n    Returns:\n        np.ndarray: Shifted bounding boxes with the same shape as input.\n    \"\"\"\n    # Create a copy of the input array to avoid modifying it in-place\n    shifted_bboxes = bboxes.copy()\n\n    # Add the shift vector to the first 4 columns\n    shifted_bboxes[:, :4] += shift_vector\n\n    return shifted_bboxes\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.shuffle_tiles_within_shape_groups","title":"def shuffle_tiles_within_shape_groups (shape_groups, random_generator) [view source on GitHub]","text":"

Shuffles indices within each group of similar shapes and creates a list where each index points to the index of the tile it should be mapped to.

Parameters:

Name Type Description shape_groups dict[tuple[int, int], list[int]]

Groups of tile indices categorized by shape.

random_generator np.random.Generator

The random generator to use for shuffling the indices. If None, a new random generator will be used.

Returns:

Type Description list[int]

A list where each index is mapped to the new index of the tile after shuffling.

Source code in albumentations/augmentations/geometric/functional.py Python
def shuffle_tiles_within_shape_groups(\n    shape_groups: dict[tuple[int, int], list[int]],\n    random_generator: np.random.Generator,\n) -> list[int]:\n    \"\"\"Shuffles indices within each group of similar shapes and creates a list where each\n    index points to the index of the tile it should be mapped to.\n\n    Args:\n        shape_groups (dict[tuple[int, int], list[int]]): Groups of tile indices categorized by shape.\n        random_generator (np.random.Generator): The random generator to use for shuffling the indices.\n            If None, a new random generator will be used.\n\n    Returns:\n        list[int]: A list where each index is mapped to the new index of the tile after shuffling.\n    \"\"\"\n    # Initialize the output list with the same size as the total number of tiles, filled with -1\n    num_tiles = sum(len(indices) for indices in shape_groups.values())\n    mapping = [-1] * num_tiles\n\n    # Prepare the random number generator\n\n    for indices in shape_groups.values():\n        shuffled_indices = indices.copy()\n        random_generator.shuffle(shuffled_indices)\n\n        for old, new in zip(indices, shuffled_indices):\n            mapping[old] = new\n\n    return mapping\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.split_uniform_grid","title":"def split_uniform_grid (image_shape, grid, random_generator) [view source on GitHub]","text":"

Splits an image shape into a uniform grid specified by the grid dimensions.

Parameters:

Name Type Description image_shape tuple[int, int]

The shape of the image as (height, width).

grid tuple[int, int]

The grid size as (rows, columns).

random_generator np.random.Generator

The random generator to use for shuffling the splits. If None, the splits are not shuffled.

Returns:

Type Description np.ndarray

An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).

Note

The function uses generate_shuffled_splits to generate the splits for the height and width of the image. The splits are then used to calculate the coordinates of the tiles.

Source code in albumentations/augmentations/geometric/functional.py Python
def split_uniform_grid(\n    image_shape: tuple[int, int],\n    grid: tuple[int, int],\n    random_generator: np.random.Generator,\n) -> np.ndarray:\n    \"\"\"Splits an image shape into a uniform grid specified by the grid dimensions.\n\n    Args:\n        image_shape (tuple[int, int]): The shape of the image as (height, width).\n        grid (tuple[int, int]): The grid size as (rows, columns).\n        random_generator (np.random.Generator): The random generator to use for shuffling the splits.\n            If None, the splits are not shuffled.\n\n    Returns:\n        np.ndarray: An array containing the tiles' coordinates in the format (start_y, start_x, end_y, end_x).\n\n    Note:\n        The function uses `generate_shuffled_splits` to generate the splits for the height and width of the image.\n        The splits are then used to calculate the coordinates of the tiles.\n    \"\"\"\n    n_rows, n_cols = grid\n\n    height_splits = generate_shuffled_splits(\n        image_shape[0],\n        grid[0],\n        random_generator=random_generator,\n    )\n    width_splits = generate_shuffled_splits(\n        image_shape[1],\n        grid[1],\n        random_generator=random_generator,\n    )\n\n    # Calculate tiles coordinates\n    tiles = [\n        (height_splits[i], width_splits[j], height_splits[i + 1], width_splits[j + 1])\n        for i in range(n_rows)\n        for j in range(n_cols)\n    ]\n\n    return np.array(tiles, dtype=np.int16)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.swap_tiles_on_image","title":"def swap_tiles_on_image (image, tiles, mapping=None) [view source on GitHub]","text":"

Swap tiles on the image according to the new format.

Parameters:

Name Type Description image np.ndarray

Input image.

tiles np.ndarray

Array of tiles with each tile as [start_y, start_x, end_y, end_x].

mapping list[int] | None

list of new tile indices.

Returns:

Type Description np.ndarray

Output image with tiles swapped according to the random shuffle.

Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_image(\n    image: np.ndarray,\n    tiles: np.ndarray,\n    mapping: list[int] | None = None,\n) -> np.ndarray:\n    \"\"\"Swap tiles on the image according to the new format.\n\n    Args:\n        image: Input image.\n        tiles: Array of tiles with each tile as [start_y, start_x, end_y, end_x].\n        mapping: list of new tile indices.\n\n    Returns:\n        np.ndarray: Output image with tiles swapped according to the random shuffle.\n    \"\"\"\n    # If no tiles are provided, return a copy of the original image\n    if tiles.size == 0 or mapping is None:\n        return image.copy()\n\n    # Create a copy of the image to retain original for reference\n    new_image = np.empty_like(image)\n    for num, new_index in enumerate(mapping):\n        start_y, start_x, end_y, end_x = tiles[new_index]\n        start_y_orig, start_x_orig, end_y_orig, end_x_orig = tiles[num]\n        # Assign the corresponding tile from the original image to the new image\n        new_image[start_y:end_y, start_x:end_x] = image[\n            start_y_orig:end_y_orig,\n            start_x_orig:end_x_orig,\n        ]\n\n    return new_image\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.swap_tiles_on_keypoints","title":"def swap_tiles_on_keypoints (keypoints, tiles, mapping) [view source on GitHub]","text":"

Swap the positions of keypoints based on a tile mapping.

This function takes a set of keypoints and repositions them according to a mapping of tile swaps. Keypoints are moved from their original tiles to new positions in the swapped tiles.

Parameters:

Name Type Description keypoints np.ndarray

A 2D numpy array of shape (N, 2) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

tiles np.ndarray

A 2D numpy array of shape (M, 4) where M is the number of tiles. Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.

mapping np.ndarray

A 1D numpy array of shape (M,) where M is the number of tiles. Each element i contains the index of the tile that tile i should be swapped with.

Returns:

Type Description np.ndarray

A 2D numpy array of the same shape as the input keypoints, containing the new positions of the keypoints after the tile swap.

Exceptions:

Type Description RuntimeWarning

If any keypoint is not found within any tile.

Notes

  • Keypoints that do not fall within any tile will remain unchanged.
  • The function assumes that the tiles do not overlap and cover the entire image space.
Source code in albumentations/augmentations/geometric/functional.py Python
def swap_tiles_on_keypoints(\n    keypoints: np.ndarray,\n    tiles: np.ndarray,\n    mapping: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Swap the positions of keypoints based on a tile mapping.\n\n    This function takes a set of keypoints and repositions them according to a mapping of tile swaps.\n    Keypoints are moved from their original tiles to new positions in the swapped tiles.\n\n    Args:\n        keypoints (np.ndarray): A 2D numpy array of shape (N, 2) where N is the number of keypoints.\n                                Each row represents a keypoint's (x, y) coordinates.\n        tiles (np.ndarray): A 2D numpy array of shape (M, 4) where M is the number of tiles.\n                            Each row represents a tile's (start_y, start_x, end_y, end_x) coordinates.\n        mapping (np.ndarray): A 1D numpy array of shape (M,) where M is the number of tiles.\n                              Each element i contains the index of the tile that tile i should be swapped with.\n\n    Returns:\n        np.ndarray: A 2D numpy array of the same shape as the input keypoints, containing the new positions\n                    of the keypoints after the tile swap.\n\n    Raises:\n        RuntimeWarning: If any keypoint is not found within any tile.\n\n    Notes:\n        - Keypoints that do not fall within any tile will remain unchanged.\n        - The function assumes that the tiles do not overlap and cover the entire image space.\n    \"\"\"\n    if not keypoints.size:\n        return keypoints\n\n    # Broadcast keypoints and tiles for vectorized comparison\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    start_y, start_x, end_y, end_x = tiles.T  # Each shape: (num_tiles,)\n\n    # Check if each keypoint is inside each tile\n    in_tile = (kp_y >= start_y) & (kp_y < end_y) & (kp_x >= start_x) & (kp_x < end_x)\n\n    # Find which tile each keypoint belongs to\n    tile_indices = np.argmax(in_tile, axis=1)\n\n    # Check if any keypoint is not in any tile\n    not_in_any_tile = ~np.any(in_tile, axis=1)\n    if np.any(not_in_any_tile):\n        warn(\n            \"Some keypoints are not in any tile. They will be returned unchanged. This is unexpected and should be \"\n            \"investigated.\",\n            RuntimeWarning,\n            stacklevel=2,\n        )\n\n    # Get the new tile indices\n    new_tile_indices = np.array(mapping)[tile_indices]\n\n    # Calculate the offsets\n    old_start_x = tiles[tile_indices, 1]\n    old_start_y = tiles[tile_indices, 0]\n    new_start_x = tiles[new_tile_indices, 1]\n    new_start_y = tiles[new_tile_indices, 0]\n\n    # Apply the transformation\n    new_keypoints = keypoints.copy()\n    new_keypoints[:, 0] = (keypoints[:, 0] - old_start_x) + new_start_x\n    new_keypoints[:, 1] = (keypoints[:, 1] - old_start_y) + new_start_y\n\n    # Keep original coordinates for keypoints not in any tile\n    new_keypoints[not_in_any_tile] = keypoints[not_in_any_tile]\n\n    return new_keypoints\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.to_distance_maps","title":"def to_distance_maps (keypoints, image_shape, inverted=False) [view source on GitHub]","text":"

Generate a (H,W,N) array of distance maps for N keypoints.

The n-th distance map contains at every location (y, x) the euclidean distance to the n-th keypoint.

This function can be used as a helper when augmenting keypoints with a method that only supports the augmentation of images.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 2+) where N is the number of keypoints. Each row represents a keypoint's (x, y) coordinates.

image_shape tuple[int, int]

tuple[int, int] shape of the image (height, width)

inverted bool

If True, inverted distance maps are returned where each distance value d is replaced by d/(d+1), i.e. the distance maps have values in the range (0.0, 1.0] with 1.0 denoting exactly the position of the respective keypoint.

Returns:

Type Description np.ndarray

A float32 array of shape (H, W, N) containing N distance maps for N keypoints. Each location (y, x, n) in the array denotes the euclidean distance at (y, x) to the n-th keypoint. If inverted is True, the distance d is replaced by d/(d+1). The height and width of the array match the height and width in image_shape.

Source code in albumentations/augmentations/geometric/functional.py Python
def to_distance_maps(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n    inverted: bool = False,\n) -> np.ndarray:\n    \"\"\"Generate a ``(H,W,N)`` array of distance maps for ``N`` keypoints.\n\n    The ``n``-th distance map contains at every location ``(y, x)`` the\n    euclidean distance to the ``n``-th keypoint.\n\n    This function can be used as a helper when augmenting keypoints with a\n    method that only supports the augmentation of images.\n\n    Args:\n        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.\n                   Each row represents a keypoint's (x, y) coordinates.\n        image_shape: tuple[int, int] shape of the image (height, width)\n        inverted (bool): If ``True``, inverted distance maps are returned where each\n            distance value d is replaced by ``d/(d+1)``, i.e. the distance\n            maps have values in the range ``(0.0, 1.0]`` with ``1.0`` denoting\n            exactly the position of the respective keypoint.\n\n    Returns:\n        np.ndarray: A ``float32`` array of shape (H, W, N) containing ``N`` distance maps for ``N``\n            keypoints. Each location ``(y, x, n)`` in the array denotes the\n            euclidean distance at ``(y, x)`` to the ``n``-th keypoint.\n            If `inverted` is ``True``, the distance ``d`` is replaced\n            by ``d/(d+1)``. The height and width of the array match the\n            height and width in ``image_shape``.\n    \"\"\"\n    height, width = image_shape[:2]\n    if len(keypoints) == 0:\n        return np.zeros((height, width, 0), dtype=np.float32)\n\n    # Create coordinate grids\n    yy, xx = np.mgrid[:height, :width]\n\n    # Convert keypoints to numpy array\n    keypoints_array = np.array(keypoints)\n\n    # Compute distances for all keypoints at once\n    distances = np.sqrt(\n        (xx[..., np.newaxis] - keypoints_array[:, 0]) ** 2 + (yy[..., np.newaxis] - keypoints_array[:, 1]) ** 2,\n    )\n\n    if inverted:\n        return (1 / (distances + 1)).astype(np.float32)\n    return distances.astype(np.float32)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.tps_transform","title":"def tps_transform (target_points, control_points, nonlinear_weights, affine_weights) [view source on GitHub]","text":"

Apply Thin Plate Spline transformation to points.

Parameters:

Name Type Description target_points np.ndarray

Points to transform with shape (num_targets, 2)

control_points np.ndarray

Original control points with shape (num_controls, 2)

nonlinear_weights np.ndarray

TPS kernel weights with shape (num_controls, 2)

affine_weights np.ndarray

Affine transformation weights with shape (3, 2)

Returns:

Type Description np.ndarray

Transformed points with shape (num_targets, 2)

Note

The transformation combines: 1. Nonlinear warping based on distances to control points 2. Global affine transformation (scale, rotation, translation)

Source code in albumentations/augmentations/geometric/functional.py Python
def tps_transform(\n    target_points: np.ndarray,\n    control_points: np.ndarray,\n    nonlinear_weights: np.ndarray,\n    affine_weights: np.ndarray,\n) -> np.ndarray:\n    \"\"\"Apply Thin Plate Spline transformation to points.\n\n    Args:\n        target_points: Points to transform with shape (num_targets, 2)\n        control_points: Original control points with shape (num_controls, 2)\n        nonlinear_weights: TPS kernel weights with shape (num_controls, 2)\n        affine_weights: Affine transformation weights with shape (3, 2)\n\n    Returns:\n        Transformed points with shape (num_targets, 2)\n\n    Note:\n        The transformation combines:\n        1. Nonlinear warping based on distances to control points\n        2. Global affine transformation (scale, rotation, translation)\n    \"\"\"\n    # Compute all pairwise distances at once: (num_targets, num_controls)\n    distances = np.linalg.norm(target_points[:, None] - control_points, axis=2)\n\n    # Apply TPS kernel function: U(r) = r\u00b2 log(r)\n    kernel_matrix = np.where(\n        distances > 0,\n        distances * distances * np.log(distances + 1e-6),\n        0,\n    )\n\n    # Prepare affine terms [1, x, y] for each point\n    affine_terms = np.c_[np.ones(len(target_points)), target_points]\n\n    # Combine nonlinear and affine transformations\n    return kernel_matrix @ nonlinear_weights + affine_terms @ affine_weights\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.transpose","title":"def transpose (img) [view source on GitHub]","text":"

Transposes the first two dimensions of an array of any dimensionality. Retains the order of any additional dimensions.

Parameters:

Name Type Description img np.ndarray

Input array.

Returns:

Type Description np.ndarray

Transposed array.

Source code in albumentations/augmentations/geometric/functional.py Python
def transpose(img: np.ndarray) -> np.ndarray:\n    \"\"\"Transposes the first two dimensions of an array of any dimensionality.\n    Retains the order of any additional dimensions.\n\n    Args:\n        img (np.ndarray): Input array.\n\n    Returns:\n        np.ndarray: Transposed array.\n    \"\"\"\n    # Generate the new axes order\n    new_axes = list(range(img.ndim))\n    new_axes[0], new_axes[1] = 1, 0  # Swap the first two dimensions\n\n    # Transpose the array using the new axes order\n    return img.transpose(new_axes)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_bboxes","title":"def validate_bboxes (bboxes, image_shape) [view source on GitHub]","text":"

Validate bounding boxes and remove invalid ones.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid bounding boxes, potentially with fewer boxes than the input.

Examples:

Python
>>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n>>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n>>> print(valid_bboxes)\n[[10 20 30 40]]\n
Source code in albumentations/augmentations/geometric/functional.py Python
def validate_bboxes(bboxes: np.ndarray, image_shape: Sequence[int]) -> np.ndarray:\n    \"\"\"Validate bounding boxes and remove invalid ones.\n\n    Args:\n        bboxes (np.ndarray): Array of bounding boxes with shape (n, 4) where each row is [x_min, y_min, x_max, y_max].\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid bounding boxes, potentially with fewer boxes than the input.\n\n    Example:\n        >>> bboxes = np.array([[10, 20, 30, 40], [-10, -10, 5, 5], [100, 100, 120, 120]])\n        >>> valid_bboxes = validate_bboxes(bboxes, (100, 100))\n        >>> print(valid_bboxes)\n        [[10 20 30 40]]\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x_min, y_min, x_max, y_max = bboxes[:, 0], bboxes[:, 1], bboxes[:, 2], bboxes[:, 3]\n\n    valid_indices = (x_max > 0) & (y_max > 0) & (x_min < cols) & (y_min < rows)\n\n    return bboxes[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_if_not_found_coords","title":"def validate_if_not_found_coords (if_not_found_coords) [view source on GitHub]","text":"

Validate and process if_not_found_coords parameter.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_if_not_found_coords(\n    if_not_found_coords: Sequence[int] | dict[str, Any] | None,\n) -> tuple[bool, float, float]:\n    \"\"\"Validate and process `if_not_found_coords` parameter.\"\"\"\n    if if_not_found_coords is None:\n        return True, -1, -1\n    if isinstance(if_not_found_coords, (tuple, list)):\n        if len(if_not_found_coords) != PAIR:\n            msg = \"Expected tuple/list 'if_not_found_coords' to contain exactly two entries.\"\n            raise ValueError(msg)\n        return False, if_not_found_coords[0], if_not_found_coords[1]\n    if isinstance(if_not_found_coords, dict):\n        return False, if_not_found_coords[\"x\"], if_not_found_coords[\"y\"]\n\n    msg = \"Expected if_not_found_coords to be None, tuple, list, or dict.\"\n    raise ValueError(msg)\n
"},{"location":"api_reference/augmentations/geometric/functional/#albumentations.augmentations.geometric.functional.validate_keypoints","title":"def validate_keypoints (keypoints, image_shape) [view source on GitHub]","text":"

Validate keypoints and remove those that fall outside the image boundaries.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, M) where N is the number of keypoints and M >= 2. The first two columns represent x and y coordinates.

image_shape tuple[int, int]

Shape of the image as (height, width).

Returns:

Type Description np.ndarray

Array of valid keypoints that fall within the image boundaries.

Note

This function only checks the x and y coordinates (first two columns) of the keypoints. Any additional columns (e.g., angle, scale) are preserved for valid keypoints.

Source code in albumentations/augmentations/geometric/functional.py Python
def validate_keypoints(\n    keypoints: np.ndarray,\n    image_shape: tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Validate keypoints and remove those that fall outside the image boundaries.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, M) where N is the number of keypoints\n                                and M >= 2. The first two columns represent x and y coordinates.\n        image_shape (tuple[int, int]): Shape of the image as (height, width).\n\n    Returns:\n        np.ndarray: Array of valid keypoints that fall within the image boundaries.\n\n    Note:\n        This function only checks the x and y coordinates (first two columns) of the keypoints.\n        Any additional columns (e.g., angle, scale) are preserved for valid keypoints.\n    \"\"\"\n    rows, cols = image_shape[:2]\n\n    x, y = keypoints[:, 0], keypoints[:, 1]\n\n    valid_indices = (x >= 0) & (x < cols) & (y >= 0) & (y < rows)\n\n    return keypoints[valid_indices]\n
"},{"location":"api_reference/augmentations/geometric/resize/","title":"Resizing transforms (augmentations.geometric.resize)","text":""},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.LongestMaxSize","title":"class LongestMaxSize [view source on GitHub]","text":"

Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, Sequence[int]

Maximum size of the longest side after the transformation. When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must fit within these bounds - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

interpolation method. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the longest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / max(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = min(H_target/H, W_target/W)\n       This ensures both dimensions fit within the specified bounds.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import albumentations as A\n>>> import cv2\n>>> # Using max_size\n>>> transform1 = A.LongestMaxSize(max_size=1024)\n>>> # Input image (1500, 800) -> Output (1024, 546)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>> # Input (800, 1500) -> Output (546, 1024)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n>>> # Input (1500, 800) -> Output (800, 427)\n>>>\n>>> # Common use case with padding\n>>> transform4 = A.Compose([\n...     A.LongestMaxSize(max_size=1024),\n...     A.PadIfNeeded(min_height=1024, min_width=1024),\n... ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class LongestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that the longest side is equal to max_size or sides meet max_size_hw constraints,\n        keeping the aspect ratio.\n\n    Args:\n        max_size (int, Sequence[int], optional): Maximum size of the longest side after the transformation.\n            When using a list or tuple, the max size will be randomly selected from the values provided. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must fit within these bounds\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): interpolation method. Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the longest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be smaller than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / max(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = min(H_target/H, W_target/W)\n               This ensures both dimensions fit within the specified bounds.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import albumentations as A\n        >>> import cv2\n        >>> # Using max_size\n        >>> transform1 = A.LongestMaxSize(max_size=1024)\n        >>> # Input image (1500, 800) -> Output (1024, 546)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.LongestMaxSize(max_size_hw=(800, 1024))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>> # Input (800, 1500) -> Output (546, 1024)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.LongestMaxSize(max_size_hw=(800, None))\n        >>> # Input (1500, 800) -> Output (800, 427)\n        >>>\n        >>> # Common use case with padding\n        >>> transform4 = A.Compose([\n        ...     A.LongestMaxSize(max_size=1024),\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024),\n        ... ])\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / max(img_h, img_w)\n        elif self.max_size_hw is not None:\n            # We know max_size_hw is not None here due to model validator\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on longest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = min(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.MaxSizeTransform","title":"class MaxSizeTransform (max_size=1024, max_size_hw=None, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Base class for transforms that resize based on maximum size constraints.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class MaxSizeTransform(DualTransform):\n    \"\"\"Base class for transforms that resize based on maximum size constraints.\"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        max_size: int | list[int] | None\n        max_size_hw: tuple[int | None, int | None] | None\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def validate_size_parameters(self) -> Self:\n            if self.max_size is None and self.max_size_hw is None:\n                raise ValueError(\"Either max_size or max_size_hw must be specified\")\n            if self.max_size is not None and self.max_size_hw is not None:\n                raise ValueError(\"Only one of max_size or max_size_hw should be specified\")\n            return self\n\n    def __init__(\n        self,\n        max_size: int | Sequence[int] | None = 1024,\n        max_size_hw: tuple[int | None, int | None] | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.max_size = max_size\n        self.max_size_hw = max_size_hw\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = img.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(img, (new_height, new_width), interpolation=self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        height, width = mask.shape[:2]\n        new_height, new_width = max(1, round(height * scale)), max(1, round(width * scale))\n        return fgeometric.resize(mask, (new_height, new_width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(images, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volume, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(volumes, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply_to_mask(masks3d, *args, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"max_size\", \"max_size_hw\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.RandomScale","title":"class RandomScale (scale_limit=(-0.1, 0.1), interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly resize the input. Output image size is different from the input image size.

Parameters:

Name Type Description scale_limit float or tuple[float, float]

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The output image size is different from the input image size.
  • Scale factor is sampled independently per image side (width and height).
  • Bounding box coordinates are scaled accordingly.
  • Keypoint coordinates are scaled accordingly.

Mathematical formulation: Let (W, H) be the original image dimensions and (W', H') be the output dimensions. The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]]. Then, W' = W * s and H' = H * s.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n>>> result = transform(image=image)\n>>> scaled_image = result['image']\n# scaled_image will have dimensions in the range [90, 110] x [90, 110]\n# (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class RandomScale(DualTransform):\n    \"\"\"Randomly resize the input. Output image size is different from the input image size.\n\n    Args:\n        scale_limit (float or tuple[float, float]): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The output image size is different from the input image size.\n        - Scale factor is sampled independently per image side (width and height).\n        - Bounding box coordinates are scaled accordingly.\n        - Keypoint coordinates are scaled accordingly.\n\n    Mathematical formulation:\n        Let (W, H) be the original image dimensions and (W', H') be the output dimensions.\n        The scale factor s is sampled from the range [1 + scale_limit[0], 1 + scale_limit[1]].\n        Then, W' = W * s and H' = H * s.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.RandomScale(scale_limit=0.1, p=1.0)\n        >>> result = transform(image=image)\n        >>> scaled_image = result['image']\n        # scaled_image will have dimensions in the range [90, 110] x [90, 110]\n        # (assuming the scale_limit of 0.1 results in a scaling factor between 0.9 and 1.1)\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale_limit: ScaleFloatType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(cls, v: ScaleFloatType) -> tuple[float, float]:\n            return to_tuple(v, bias=1.0)\n\n    def __init__(\n        self,\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def get_params(self) -> dict[str, float]:\n        return {\"scale\": self.py_random.uniform(*self.scale_limit)}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(img, scale, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.scale(mask, scale, self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        scale: float,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_scale(keypoints, scale, scale)\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"interpolation\": self.interpolation,\n            \"mask_interpolation\": self.mask_interpolation,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n        }\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.Resize","title":"class Resize (height, width, interpolation=1, mask_interpolation=0, p=1, always_apply=None) [view source on GitHub]","text":"

Resize the input to the given height and width.

Parameters:

Name Type Description height int

desired height of the output.

width int

desired width of the output.

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class Resize(DualTransform):\n    \"\"\"Resize the input to the given height and width.\n\n    Args:\n        height (int): desired height of the output.\n        width (int): desired width of the output.\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        height: int = Field(ge=1)\n        width: int = Field(ge=1)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        height: int,\n        width: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.height = height\n        self.width = width\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(img, (self.height, self.width), interpolation=self.interpolation)\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.resize(mask, (self.height, self.width), interpolation=self.mask_interpolation)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        # Bounding box coordinates are scale invariant\n        return bboxes\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        height, width = params[\"shape\"][:2]\n        scale_x = self.width / width\n        scale_y = self.height / height\n        return fgeometric.keypoints_scale(keypoints, scale_x, scale_y)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"height\", \"width\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/resize/#albumentations.augmentations.geometric.resize.SmallestMaxSize","title":"class SmallestMaxSize [view source on GitHub]","text":"

Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints, keeping the aspect ratio.

Parameters:

Name Type Description max_size int, list of int

Maximum size of smallest side of the image after the transformation. When using a list, max size will be randomly selected from the values in the list. Default: 1024.

max_size_hw tuple[int | None, int | None]

Maximum (height, width) constraints. Supports: - (height, width): Both dimensions must be at least these values - (height, None): Only height is constrained, width scales proportionally - (None, width): Only width is constrained, height scales proportionally If specified, max_size must be None. Default: None.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 1.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • If the smallest side of the image is already equal to max_size, the image will not be resized.
  • This transform will not crop the image. The resulting image may be larger than specified in both dimensions.
  • For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.
  • Bounding boxes and keypoints are scaled accordingly.

Mathematical Details: Let (W, H) be the original width and height of the image.

When using max_size:\n    1. The scaling factor s is calculated as:\n       s = max_size / min(W, H)\n    2. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n\nWhen using max_size_hw=(H_target, W_target):\n    1. For both dimensions specified:\n       s = max(H_target/H, W_target/W)\n       This ensures both dimensions are at least as large as specified.\n\n    2. For height only (W_target=None):\n       s = H_target/H\n       Width will scale proportionally.\n\n    3. For width only (H_target=None):\n       s = W_target/W\n       Height will scale proportionally.\n\n    4. The new dimensions (W', H') are:\n       W' = W * s\n       H' = H * s\n

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> # Using max_size\n>>> transform1 = A.SmallestMaxSize(max_size=120)\n>>> # Input image (100, 150) -> Output (120, 180)\n>>>\n>>> # Using max_size_hw with both dimensions\n>>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n>>> # Input (80, 160) -> Output (100, 200)\n>>> # Input (160, 80) -> Output (400, 200)\n>>>\n>>> # Using max_size_hw with only height\n>>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n>>> # Input (80, 160) -> Output (100, 200)\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/resize.py Python
class SmallestMaxSize(MaxSizeTransform):\n    \"\"\"Rescale an image so that minimum side is equal to max_size or sides meet max_size_hw constraints,\n    keeping the aspect ratio.\n\n    Args:\n        max_size (int, list of int, optional): Maximum size of smallest side of the image after the transformation.\n            When using a list, max size will be randomly selected from the values in the list. Default: 1024.\n        max_size_hw (tuple[int | None, int | None], optional): Maximum (height, width) constraints. Supports:\n            - (height, width): Both dimensions must be at least these values\n            - (height, None): Only height is constrained, width scales proportionally\n            - (None, width): Only width is constrained, height scales proportionally\n            If specified, max_size must be None. Default: None.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 1.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - If the smallest side of the image is already equal to max_size, the image will not be resized.\n        - This transform will not crop the image. The resulting image may be larger than specified in both dimensions.\n        - For non-square images, both sides will be scaled proportionally to maintain the aspect ratio.\n        - Bounding boxes and keypoints are scaled accordingly.\n\n    Mathematical Details:\n        Let (W, H) be the original width and height of the image.\n\n        When using max_size:\n            1. The scaling factor s is calculated as:\n               s = max_size / min(W, H)\n            2. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n        When using max_size_hw=(H_target, W_target):\n            1. For both dimensions specified:\n               s = max(H_target/H, W_target/W)\n               This ensures both dimensions are at least as large as specified.\n\n            2. For height only (W_target=None):\n               s = H_target/H\n               Width will scale proportionally.\n\n            3. For width only (H_target=None):\n               s = W_target/W\n               Height will scale proportionally.\n\n            4. The new dimensions (W', H') are:\n               W' = W * s\n               H' = H * s\n\n    Examples:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> # Using max_size\n        >>> transform1 = A.SmallestMaxSize(max_size=120)\n        >>> # Input image (100, 150) -> Output (120, 180)\n        >>>\n        >>> # Using max_size_hw with both dimensions\n        >>> transform2 = A.SmallestMaxSize(max_size_hw=(100, 200))\n        >>> # Input (80, 160) -> Output (100, 200)\n        >>> # Input (160, 80) -> Output (400, 200)\n        >>>\n        >>> # Using max_size_hw with only height\n        >>> transform3 = A.SmallestMaxSize(max_size_hw=(100, None))\n        >>> # Input (80, 160) -> Output (100, 200)\n    \"\"\"\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        img_h, img_w = params[\"shape\"][:2]\n\n        if self.max_size is not None:\n            if isinstance(self.max_size, (list, tuple)):\n                max_size = self.py_random.choice(self.max_size)\n            else:\n                max_size = self.max_size\n            scale = max_size / min(img_h, img_w)\n        elif self.max_size_hw is not None:\n            max_h, max_w = self.max_size_hw\n            if max_h is not None and max_w is not None:\n                # Scale based on smallest side to maintain aspect ratio\n                h_scale = max_h / img_h\n                w_scale = max_w / img_w\n                scale = max(h_scale, w_scale)\n            elif max_h is not None:\n                # Only height specified\n                scale = max_h / img_h\n            else:\n                # Only width specified\n                scale = max_w / img_w\n\n        return {\"scale\": scale}\n
"},{"location":"api_reference/augmentations/geometric/rotate/","title":"Rotation transforms (augmentations.geometric.functional)","text":""},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.RandomRotate90","title":"class RandomRotate90 [view source on GitHub]","text":"

Randomly rotate the input by 90 degrees zero or more times.

Parameters:

Name Type Description p

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RandomRotate90(DualTransform):\n    \"\"\"Randomly rotate the input by 90 degrees zero or more times.\n\n    Args:\n        p: probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, factor: Literal[0, 1, 2, 3], **params: Any) -> np.ndarray:\n        return fgeometric.rot90(img, factor)\n\n    def get_params(self) -> dict[str, int]:\n        # Random int in the range [0, 3]\n        return {\"factor\": self.py_random.randint(0, 3)}\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        factor: Literal[0, 1, 2, 3],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_rot90(bboxes, factor)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        factor: Literal[0, 1, 2, 3],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_rot90(keypoints, factor, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.Rotate","title":"class Rotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', crop_border=False, mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input by an angle selected randomly from the uniform distribution.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method str

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

crop_border bool

Whether to crop border after rotation. If True, the output image size might differ from the input. Default: False

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation angle is randomly selected for each execution within the range specified by 'limit'.
  • When 'crop_border' is False, the output image will have the same size as the input, potentially introducing black triangles in the corners.
  • When 'crop_border' is True, the output image is cropped to remove black triangles, which may result in a smaller image.
  • Bounding boxes are rotated and may change size or shape.
  • Keypoints are rotated around the center of the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. Each point (x, y) in the image is transformed to (x', y') by: [x'] cos(\u03b8) -sin(\u03b8) [cx] [y'] = sin(\u03b8) cos(\u03b8) + [cy] where (cx, cy) is the center of the image. 5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Rotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class Rotate(DualTransform):\n    \"\"\"Rotate the input by an angle selected randomly from the uniform distribution.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        rotate_method (str): Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'.\n            Default: 'largest_box'\n        crop_border (bool): Whether to crop border after rotation. If True, the output image size might differ\n            from the input. Default: False\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation angle is randomly selected for each execution within the range specified by 'limit'.\n        - When 'crop_border' is False, the output image will have the same size as the input, potentially\n          introducing black triangles in the corners.\n        - When 'crop_border' is True, the output image is cropped to remove black triangles, which may result\n          in a smaller image.\n        - Bounding boxes are rotated and may change size or shape.\n        - Keypoints are rotated around the center of the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [cos(\u03b8)  -sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [sin(\u03b8)   cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n        5. If 'crop_border' is True, the image is cropped to the largest rectangle that fits inside the rotated image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Rotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        crop_border: bool\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        @model_validator(mode=\"after\")\n        def validate_value(self) -> Self:\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        crop_border: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.crop_border = crop_border\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            img,\n            matrix,\n            self.interpolation,\n            self.fill,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        img_out = fgeometric.warp_affine(\n            mask,\n            matrix,\n            self.mask_interpolation,\n            self.fill_mask,\n            self.border_mode,\n            params[\"shape\"][:2],\n        )\n        if self.crop_border:\n            return fcrops.crop(img_out, x_min, y_min, x_max, y_max)\n        return img_out\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_out = fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            image_shape,\n            self.border_mode,\n            image_shape,\n        )\n        if self.crop_border:\n            return fcrops.crop_bboxes_by_coords(\n                bboxes_out,\n                (x_min, y_min, x_max, y_max),\n                image_shape,\n            )\n        return bboxes_out\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        x_min: int,\n        x_max: int,\n        y_min: int,\n        y_max: int,\n        **params: Any,\n    ) -> np.ndarray:\n        keypoints_out = fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"][:2],\n            scale={\"x\": 1, \"y\": 1},\n            border_mode=self.border_mode,\n        )\n        if self.crop_border:\n            return fcrops.crop_keypoints_by_coords(\n                keypoints_out,\n                (x_min, y_min, x_max, y_max),\n            )\n        return keypoints_out\n\n    @staticmethod\n    def _rotated_rect_with_max_area(\n        height: int,\n        width: int,\n        angle: float,\n    ) -> dict[str, int]:\n        \"\"\"Given a rectangle of size wxh that has been rotated by 'angle' (in\n        degrees), computes the width and height of the largest possible\n        axis-aligned rectangle (maximal area) within the rotated rectangle.\n\n        Reference:\n            https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders\n        \"\"\"\n        angle = math.radians(angle)\n        width_is_longer = width >= height\n        side_long, side_short = (width, height) if width_is_longer else (height, width)\n\n        # since the solutions for angle, -angle and 180-angle are all the same,\n        # it is sufficient to look at the first quadrant and the absolute values of sin,cos:\n        sin_a, cos_a = abs(math.sin(angle)), abs(math.cos(angle))\n        if side_short <= 2.0 * sin_a * cos_a * side_long or abs(sin_a - cos_a) < SMALL_NUMBER:\n            # half constrained case: two crop corners touch the longer side,\n            # the other two corners are on the mid-line parallel to the longer line\n            x = 0.5 * side_short\n            wr, hr = (x / sin_a, x / cos_a) if width_is_longer else (x / cos_a, x / sin_a)\n        else:\n            # fully constrained case: crop touches all 4 sides\n            cos_2a = cos_a * cos_a - sin_a * sin_a\n            wr, hr = (\n                (width * cos_a - height * sin_a) / cos_2a,\n                (height * cos_a - width * sin_a) / cos_2a,\n            )\n\n        return {\n            \"x_min\": max(0, int(width / 2 - wr / 2)),\n            \"x_max\": min(width, int(width / 2 + wr / 2)),\n            \"y_min\": max(0, int(height / 2 - hr / 2)),\n            \"y_max\": min(height, int(height / 2 + hr / 2)),\n        }\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        angle = self.py_random.uniform(*self.limit)\n\n        if self.crop_border:\n            height, width = params[\"shape\"][:2]\n            out_params = self._rotated_rect_with_max_area(height, width, angle)\n        else:\n            out_params = {\"x_min\": -1, \"x_max\": -1, \"y_min\": -1, \"y_max\": -1}\n\n        center = fgeometric.center(params[\"shape\"][:2])\n        bbox_center = fgeometric.center_bbox(params[\"shape\"][:2])\n\n        translate: fgeometric.XYInt = {\"x\": 0, \"y\": 0}\n        shear: fgeometric.XYFloat = {\"x\": 0, \"y\": 0}\n        scale: fgeometric.XYFloat = {\"x\": 1, \"y\": 1}\n        rotate = angle\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            center,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_center,\n        )\n        out_params[\"matrix\"] = matrix\n        out_params[\"bbox_matrix\"] = bbox_matrix\n\n        return out_params\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"crop_border\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.RotateInitSchema","title":"class RotateInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class RotateInitSchema(BaseTransformInitSchema):\n    limit: SymmetricRangeType\n\n    interpolation: InterpolationType\n    mask_interpolation: InterpolationType\n\n    border_mode: BorderModeType\n\n    fill: ColorType | None\n    fill_mask: ColorType | None\n
"},{"location":"api_reference/augmentations/geometric/rotate/#albumentations.augmentations.geometric.rotate.SafeRotate","title":"class SafeRotate (limit=(-90, 90), interpolation=1, border_mode=4, value=None, mask_value=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.

This transformation ensures that the entire rotated image fits within the original frame by scaling it down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the rotation and scaling process.

Parameters:

Name Type Description limit float | tuple[float, float]

Range from which a random angle is picked. If limit is a single float, an angle is picked from (-limit, limit). Default: (-90, 90)

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

Flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

rotate_method Literal[\"largest_box\", \"ellipse\"]

Method to rotate bounding boxes. Should be 'largest_box' or 'ellipse'. Default: 'largest_box'

mask_interpolation OpenCV flag

flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The rotation is performed around the center of the image.
  • After rotation, the image is scaled to fit within the original frame, which may cause some distortion.
  • The output image will always have the same dimensions as the input image.
  • Bounding boxes and keypoints are transformed along with the image.

Mathematical Details: 1. An angle \u03b8 is randomly sampled from the range specified by 'limit'. 2. The image is rotated around its center by \u03b8 degrees. 3. The rotation matrix R is: R = [cos(\u03b8) -sin(\u03b8)] [sin(\u03b8) cos(\u03b8)] 4. The scaling factor s is calculated to ensure the rotated image fits within the original frame: s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|), height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|)) 5. The combined transformation matrix T is: T = [scos(\u03b8) -ssin(\u03b8) tx] [ssin(\u03b8) scos(\u03b8) ty] where tx and ty are translation factors to keep the image centered. 6. Each point (x, y) in the image is transformed to (x', y') by: [x'] scos(\u03b8) ssin(\u03b8) [cx] [y'] = -ssin(\u03b8) scos(\u03b8) + [cy] where (cx, cy) is the center of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.SafeRotate(limit=45, p=1.0)\n>>> result = transform(image=image)\n>>> rotated_image = result['image']\n# rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n# scaled to fit within the original 100x100 frame\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/rotate.py Python
class SafeRotate(Affine):\n    \"\"\"Rotate the input inside the input's frame by an angle selected randomly from the uniform distribution.\n\n    This transformation ensures that the entire rotated image fits within the original frame by scaling it\n    down if necessary. The resulting image maintains its original dimensions but may contain artifacts due to the\n    rotation and scaling process.\n\n    Args:\n        limit (float | tuple[float, float]): Range from which a random angle is picked. If limit is a single float,\n            an angle is picked from (-limit, limit). Default: (-90, 90)\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): Flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT applied\n            for masks.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): Method to rotate bounding boxes.\n            Should be 'largest_box' or 'ellipse'. Default: 'largest_box'\n        mask_interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The rotation is performed around the center of the image.\n        - After rotation, the image is scaled to fit within the original frame, which may cause some distortion.\n        - The output image will always have the same dimensions as the input image.\n        - Bounding boxes and keypoints are transformed along with the image.\n\n    Mathematical Details:\n        1. An angle \u03b8 is randomly sampled from the range specified by 'limit'.\n        2. The image is rotated around its center by \u03b8 degrees.\n        3. The rotation matrix R is:\n           R = [cos(\u03b8)  -sin(\u03b8)]\n               [sin(\u03b8)   cos(\u03b8)]\n        4. The scaling factor s is calculated to ensure the rotated image fits within the original frame:\n           s = min(width / (width * |cos(\u03b8)| + height * |sin(\u03b8)|),\n                   height / (width * |sin(\u03b8)| + height * |cos(\u03b8)|))\n        5. The combined transformation matrix T is:\n           T = [s*cos(\u03b8)  -s*sin(\u03b8)  tx]\n               [s*sin(\u03b8)   s*cos(\u03b8)  ty]\n           where tx and ty are translation factors to keep the image centered.\n        6. Each point (x, y) in the image is transformed to (x', y') by:\n           [x']   [s*cos(\u03b8)   s*sin(\u03b8)] [x - cx]   [cx]\n           [y'] = [-s*sin(\u03b8)  s*cos(\u03b8)] [y - cy] + [cy]\n           where (cx, cy) is the center of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.SafeRotate(limit=45, p=1.0)\n        >>> result = transform(image=image)\n        >>> rotated_image = result['image']\n        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,\n        # scaled to fit within the original 100x100 frame\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(RotateInitSchema):\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n\n    def __init__(\n        self,\n        limit: ScaleFloatType = (-90, 90),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            rotate=limit,\n            interpolation=interpolation,\n            border_mode=border_mode,\n            fill=fill,\n            fill_mask=fill_mask,\n            rotate_method=rotate_method,\n            fit_output=True,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.limit = cast(tuple[float, float], limit)\n        self.interpolation = interpolation\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.rotate_method = rotate_method\n        self.mask_interpolation = mask_interpolation\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"limit\",\n            \"interpolation\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"rotate_method\",\n            \"mask_interpolation\",\n        )\n\n    def _create_safe_rotate_matrix(\n        self,\n        angle: float,\n        center: tuple[float, float],\n        image_shape: tuple[int, int],\n    ) -> tuple[np.ndarray, dict[str, float]]:\n        height, width = image_shape[:2]\n        rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.0)\n\n        # Calculate new image size\n        abs_cos = abs(rotation_mat[0, 0])\n        abs_sin = abs(rotation_mat[0, 1])\n        new_w = int(height * abs_sin + width * abs_cos)\n        new_h = int(height * abs_cos + width * abs_sin)\n\n        # Adjust the rotation matrix to take into account the new size\n        rotation_mat[0, 2] += new_w / 2 - center[0]\n        rotation_mat[1, 2] += new_h / 2 - center[1]\n\n        # Calculate scaling factors\n        scale_x = width / new_w\n        scale_y = height / new_h\n\n        # Create scaling matrix\n        scale_mat = np.array([[scale_x, 0, 0], [0, scale_y, 0], [0, 0, 1]])\n\n        # Combine rotation and scaling\n        matrix = scale_mat @ np.vstack([rotation_mat, [0, 0, 1]])\n\n        return matrix, {\"x\": scale_x, \"y\": scale_y}\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        angle = self.py_random.uniform(*self.limit)\n\n        # Calculate centers for image and bbox\n        image_center = fgeometric.center(image_shape)\n        bbox_center = fgeometric.center_bbox(image_shape)\n\n        # Create matrices for image and bbox\n        matrix, scale = self._create_safe_rotate_matrix(\n            angle,\n            image_center,\n            image_shape,\n        )\n        bbox_matrix, _ = self._create_safe_rotate_matrix(\n            angle,\n            bbox_center,\n            image_shape,\n        )\n\n        return {\n            \"rotate\": angle,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": image_shape,\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/","title":"Geometric transforms (augmentations.geometric.transforms)","text":""},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Affine","title":"class Affine (scale=1, translate_percent=None, translate_px=None, rotate=0, shear=0, interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, fit_output=False, keep_ratio=False, rotate_method='largest_box', balanced_scale=False, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Augmentation to apply affine transformations to images.

Affine transformations involve:

- Translation (\"move\" image on the x-/y-axis)\n- Rotation\n- Scaling (\"zoom\" in/out)\n- Shear (move one side of the image, turning a square into a trapezoid)\n

All such transformations can create \"new\" pixels in the image without a defined content, e.g. if the image is translated to the left, pixels are created on the right. A method has to be defined to deal with these pixel values. The parameters fill and fill_mask of this class deal with this.

Some transformations involve interpolations between several pixels of the input image to generate output pixel values. The parameters interpolation and mask_interpolation deals with the method of interpolation used for this.

Parameters:

Name Type Description scale number, tuple of number or dict

Scaling factor to use, where 1.0 denotes \"no change\" and 0.5 is zoomed out to 50 percent of the original size. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That the same range will be used for both x- and y-axis. To keep the aspect ratio, set keep_ratio=True, then the same value will be used for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes. Note that when the keep_ratio=True, the x- and y-axis ranges should be the same.

translate_percent None, number, tuple of number or dict

Translation as a fraction of the image height/width (x-translation, y-translation), where 0 denotes \"no change\" and 0.5 denotes \"half of the axis size\". * If None then equivalent to 0.0 unless translate_px has a value other than None. * If a single number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b]. That sampled fraction value will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

translate_px None, int, tuple of int or dict

Translation in pixels. * If None then equivalent to 0 unless translate_percent has a value other than None. * If a single int, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the discrete interval [a..b]. That number will be used identically for both x- and y-axis. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

rotate number or tuple of number

Rotation in degrees (NOT radians), i.e. expected value range is around [-360, 360]. Rotation happens around the center of the image, not the top left corner as in some other frameworks. * If a number, then that value will be used for all images. * If a tuple (a, b), then a value will be uniformly sampled per image from the interval [a, b] and used as the rotation value.

shear number, tuple of number or dict

Shear in degrees (NOT radians), i.e. expected value range is around [-360, 360], with reasonable values being in the range of [-45, 45]. * If a number, then that value will be used for all images as the shear on the x-axis (no shear on the y-axis will be done). * If a tuple (a, b), then two value will be uniformly sampled per image from the interval [a, b] and be used as the x- and y-shear value. * If a dictionary, then it is expected to have the keys x and/or y. Each of these keys can have the same values as described above. Using a dictionary allows to set different values for the two axis and sampling will then happen independently per axis, resulting in samples that differ between the axes.

interpolation int

OpenCV interpolation flag.

mask_interpolation int

OpenCV interpolation flag.

fill ColorType

The constant value to use when filling in newly created pixels. (E.g. translating by 1px to the right will create a new 1px-wide column of pixels on the left of the image). The value is only used when mode=constant. The expected value range is [0, 255] for uint8 images.

fill_mask ColorType

Same as fill but only for masks.

border_mode int

OpenCV border flag.

fit_output bool

If True, the image plane size and position will be adjusted to tightly capture the whole image after affine transformation (translate_percent and translate_px are ignored). Otherwise (False), parts of the transformed image may end up outside the image plane. Fitting the output shape can be useful to avoid corners of the image being outside the image plane after applying rotations. Default: False

keep_ratio bool

When True, the original aspect ratio will be kept when the random scale is applied. Default: False.

rotate_method Literal[\"largest_box\", \"ellipse\"]

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"

balanced_scale bool

When True, scaling factors are chosen to be either entirely below or above 1, ensuring balanced scaling. Default: False.

This is important because without it, scaling tends to lean towards upscaling. For example, if we want the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly from [0.5, 2]. With balanced_scale, the function ensures that half the time, the scaling factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in). This makes the zooming in and out process more balanced.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Reference

[1] https://arxiv.org/abs/2109.13488

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Affine(DualTransform):\n    \"\"\"Augmentation to apply affine transformations to images.\n\n    Affine transformations involve:\n\n        - Translation (\"move\" image on the x-/y-axis)\n        - Rotation\n        - Scaling (\"zoom\" in/out)\n        - Shear (move one side of the image, turning a square into a trapezoid)\n\n    All such transformations can create \"new\" pixels in the image without a defined content, e.g.\n    if the image is translated to the left, pixels are created on the right.\n    A method has to be defined to deal with these pixel values.\n    The parameters `fill` and `fill_mask` of this class deal with this.\n\n    Some transformations involve interpolations between several pixels\n    of the input image to generate output pixel values. The parameters `interpolation` and\n    `mask_interpolation` deals with the method of interpolation used for this.\n\n    Args:\n        scale (number, tuple of number or dict): Scaling factor to use, where ``1.0`` denotes \"no change\" and\n            ``0.5`` is zoomed out to ``50`` percent of the original size.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That the same range will be used for both x- and y-axis. To keep the aspect ratio, set\n                  ``keep_ratio=True``, then the same value will be used for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes. Note that when\n                  the ``keep_ratio=True``, the x- and y-axis ranges should be the same.\n        translate_percent (None, number, tuple of number or dict): Translation as a fraction of the image height/width\n            (x-translation, y-translation), where ``0`` denotes \"no change\"\n            and ``0.5`` denotes \"half of the axis size\".\n                * If ``None`` then equivalent to ``0.0`` unless `translate_px` has a value other than ``None``.\n                * If a single number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``.\n                  That sampled fraction value will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        translate_px (None, int, tuple of int or dict): Translation in pixels.\n                * If ``None`` then equivalent to ``0`` unless `translate_percent` has a value other than ``None``.\n                * If a single int, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from\n                  the discrete interval ``[a..b]``. That number will be used identically for both x- and y-axis.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        rotate (number or tuple of number): Rotation in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``. Rotation happens around the *center* of the image,\n            not the top left corner as in some other frameworks.\n                * If a number, then that value will be used for all images.\n                * If a tuple ``(a, b)``, then a value will be uniformly sampled per image from the interval ``[a, b]``\n                  and used as the rotation value.\n        shear (number, tuple of number or dict): Shear in degrees (**NOT** radians), i.e. expected value range is\n            around ``[-360, 360]``, with reasonable values being in the range of ``[-45, 45]``.\n                * If a number, then that value will be used for all images as\n                  the shear on the x-axis (no shear on the y-axis will be done).\n                * If a tuple ``(a, b)``, then two value will be uniformly sampled per image\n                  from the interval ``[a, b]`` and be used as the x- and y-shear value.\n                * If a dictionary, then it is expected to have the keys ``x`` and/or ``y``.\n                  Each of these keys can have the same values as described above.\n                  Using a dictionary allows to set different values for the two axis and sampling will then happen\n                  *independently* per axis, resulting in samples that differ between the axes.\n        interpolation (int): OpenCV interpolation flag.\n        mask_interpolation (int): OpenCV interpolation flag.\n        fill (ColorType): The constant value to use when filling in newly created pixels.\n            (E.g. translating by 1px to the right will create a new 1px-wide column of pixels\n            on the left of the image).\n            The value is only used when `mode=constant`. The expected value range is ``[0, 255]`` for ``uint8`` images.\n        fill_mask (ColorType): Same as fill but only for masks.\n        border_mode (int): OpenCV border flag.\n        fit_output (bool): If True, the image plane size and position will be adjusted to tightly capture\n            the whole image after affine transformation (`translate_percent` and `translate_px` are ignored).\n            Otherwise (``False``),  parts of the transformed image may end up outside the image plane.\n            Fitting the output shape can be useful to avoid corners of the image being outside the image plane\n            after applying rotations. Default: False\n        keep_ratio (bool): When True, the original aspect ratio will be kept when the random scale is applied.\n            Default: False.\n        rotate_method (Literal[\"largest_box\", \"ellipse\"]): rotation method used for the bounding boxes.\n            Should be one of \"largest_box\" or \"ellipse\"[1]. Default: \"largest_box\"\n        balanced_scale (bool): When True, scaling factors are chosen to be either entirely below or above 1,\n            ensuring balanced scaling. Default: False.\n\n            This is important because without it, scaling tends to lean towards upscaling. For example, if we want\n            the image to zoom in and out by 2x, we may pick an interval [0.5, 2]. Since the interval [0.5, 1] is\n            three times smaller than [1, 2], values above 1 are picked three times more often if sampled directly\n            from [0.5, 2]. With `balanced_scale`, the  function ensures that half the time, the scaling\n            factor is picked from below 1 (zooming out), and the other half from above 1 (zooming in).\n            This makes the zooming in and out process more balanced.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        [1] https://arxiv.org/abs/2109.13488\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: ScaleFloatType | fgeometric.XYFloatScale\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None\n        rotate: ScaleFloatType\n        shear: ScaleFloatType | fgeometric.XYFloatScale\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n        cval: ColorType | None\n        cval_mask: ColorType | None\n        mode: BorderModeType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        fit_output: bool\n        keep_ratio: bool\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        balanced_scale: bool\n\n        @field_validator(\"shear\", \"scale\")\n        @classmethod\n        def process_shear(\n            cls,\n            value: ScaleFloatType | fgeometric.XYFloatScale,\n            info: ValidationInfo,\n        ) -> fgeometric.XYFloatDict:\n            return cast(\n                fgeometric.XYFloatDict,\n                cls._handle_dict_arg(value, info.field_name),\n            )\n\n        @field_validator(\"rotate\")\n        @classmethod\n        def process_rotate(\n            cls,\n            value: ScaleFloatType,\n        ) -> tuple[float, float]:\n            return to_tuple(value, value)\n\n        @model_validator(mode=\"after\")\n        def handle_translate(self) -> Self:\n            if self.translate_percent is None and self.translate_px is None:\n                self.translate_px = 0\n\n            if self.translate_percent is not None and self.translate_px is not None:\n                msg = \"Expected either translate_percent or translate_px to be provided, but both were provided.\"\n                raise ValueError(msg)\n\n            if self.translate_percent is not None:\n                self.translate_percent = self._handle_dict_arg(\n                    self.translate_percent,\n                    \"translate_percent\",\n                    default=0.0,\n                )  # type: ignore[assignment]\n\n            if self.translate_px is not None:\n                self.translate_px = self._handle_dict_arg(\n                    self.translate_px,\n                    \"translate_px\",\n                    default=0,\n                )  # type: ignore[assignment]\n\n            return self\n\n        @staticmethod\n        def _handle_dict_arg(\n            val: ScaleType | fgeometric.XYFloatScale | fgeometric.XYIntScale,\n            name: str | None,\n            default: float = 1.0,\n        ) -> dict[str, Any]:\n            if isinstance(val, dict):\n                if \"x\" not in val and \"y\" not in val:\n                    raise ValueError(\n                        f'Expected {name} dictionary to contain at least key \"x\" or key \"y\". Found neither of them.',\n                    )\n                x = val.get(\"x\", default)\n                y = val.get(\"y\", default)\n                return {\"x\": to_tuple(x, x), \"y\": to_tuple(y, y)}  # type: ignore[arg-type]\n            return {\"x\": to_tuple(val, val), \"y\": to_tuple(val, val)}\n\n        @model_validator(mode=\"after\")\n        def validate_fill_types(self) -> Self:\n            if self.cval is not None:\n                self.fill = self.cval\n                warn(\"cval is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n            if self.cval_mask is not None:\n                self.fill_mask = self.cval_mask\n                warn(\"cval_mask is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n            if self.mode is not None:\n                self.border_mode = self.mode\n                warn(\"mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType | fgeometric.XYFloatScale = 1,\n        translate_percent: ScaleFloatType | fgeometric.XYFloatScale | None = None,\n        translate_px: ScaleIntType | fgeometric.XYIntScale | None = None,\n        rotate: ScaleFloatType = 0,\n        shear: ScaleFloatType | fgeometric.XYFloatScale = 0,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: ColorType | None = None,\n        cval_mask: ColorType | None = None,\n        mode: int | None = None,\n        fit_output: bool = False,\n        keep_ratio: bool = False,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        balanced_scale: bool = False,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n        self.scale = cast(fgeometric.XYFloatDict, scale)\n        self.translate_percent = cast(fgeometric.XYFloatDict, translate_percent)\n        self.translate_px = cast(fgeometric.XYIntDict, translate_px)\n        self.rotate = cast(tuple[float, float], rotate)\n        self.fit_output = fit_output\n        self.shear = cast(fgeometric.XYFloatDict, shear)\n        self.keep_ratio = keep_ratio\n        self.rotate_method = rotate_method\n        self.balanced_scale = balanced_scale\n\n        if self.keep_ratio and self.scale[\"x\"] != self.scale[\"y\"]:\n            raise ValueError(\n                f\"When keep_ratio is True, the x and y scale range should be identical. got {self.scale}\",\n            )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"fill\",\n            \"border_mode\",\n            \"scale\",\n            \"translate_percent\",\n            \"translate_px\",\n            \"rotate\",\n            \"fit_output\",\n            \"shear\",\n            \"fill_mask\",\n            \"keep_ratio\",\n            \"rotate_method\",\n            \"balanced_scale\",\n        )\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            img,\n            matrix,\n            interpolation=self.interpolation,\n            fill=self.fill,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.warp_affine(\n            mask,\n            matrix,\n            interpolation=self.mask_interpolation,\n            fill=self.fill_mask,\n            border_mode=self.border_mode,\n            output_shape=output_shape,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        bbox_matrix: np.ndarray,\n        output_shape: tuple[int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_affine(\n            bboxes,\n            bbox_matrix,\n            self.rotate_method,\n            params[\"shape\"][:2],\n            self.border_mode,\n            output_shape,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        scale: fgeometric.XYFloat,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_affine(\n            keypoints,\n            matrix,\n            params[\"shape\"],\n            scale,\n            self.border_mode,\n        )\n\n    @staticmethod\n    def get_scale(\n        scale: fgeometric.XYFloatDict,\n        keep_ratio: bool,\n        balanced_scale: bool,\n        random_state: random.Random,\n    ) -> fgeometric.XYFloat:\n        result_scale = {}\n        for key, value in scale.items():\n            if isinstance(value, (int, float)):\n                result_scale[key] = float(value)\n            elif isinstance(value, tuple):\n                if balanced_scale:\n                    lower_interval = (value[0], 1.0) if value[0] < 1 else None\n                    upper_interval = (1.0, value[1]) if value[1] > 1 else None\n\n                    if lower_interval is not None and upper_interval is not None:\n                        selected_interval = random_state.choice(\n                            [lower_interval, upper_interval],\n                        )\n                    elif lower_interval is not None:\n                        selected_interval = lower_interval\n                    elif upper_interval is not None:\n                        selected_interval = upper_interval\n                    else:\n                        result_scale[key] = 1.0\n                        continue\n\n                    result_scale[key] = random_state.uniform(*selected_interval)\n                else:\n                    result_scale[key] = random_state.uniform(*value)\n            else:\n                raise TypeError(\n                    f\"Invalid scale value for key {key}: {value}. Expected a float or a tuple of two floats.\",\n                )\n\n        if keep_ratio:\n            result_scale[\"y\"] = result_scale[\"x\"]\n\n        return cast(fgeometric.XYFloat, result_scale)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        translate = self._get_translate_params(image_shape)\n        shear = self._get_shear_params()\n        scale = self.get_scale(\n            self.scale,\n            self.keep_ratio,\n            self.balanced_scale,\n            self.py_random,\n        )\n        rotate = self.py_random.uniform(*self.rotate)\n\n        image_shift = fgeometric.center(image_shape)\n        bbox_shift = fgeometric.center_bbox(image_shape)\n\n        matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            image_shift,\n        )\n        bbox_matrix = fgeometric.create_affine_transformation_matrix(\n            translate,\n            shear,\n            scale,\n            rotate,\n            bbox_shift,\n        )\n\n        if self.fit_output:\n            matrix, output_shape = fgeometric.compute_affine_warp_output_shape(\n                matrix,\n                image_shape,\n            )\n            bbox_matrix, _ = fgeometric.compute_affine_warp_output_shape(\n                bbox_matrix,\n                image_shape,\n            )\n        else:\n            output_shape = image_shape\n\n        return {\n            \"rotate\": rotate,\n            \"scale\": scale,\n            \"matrix\": matrix,\n            \"bbox_matrix\": bbox_matrix,\n            \"output_shape\": output_shape,\n        }\n\n    def _get_translate_params(self, image_shape: tuple[int, int]) -> fgeometric.XYInt:\n        height, width = image_shape[:2]\n        if self.translate_px is not None:\n            return {\n                \"x\": self.py_random.randint(*self.translate_px[\"x\"]),\n                \"y\": self.py_random.randint(*self.translate_px[\"y\"]),\n            }\n        if self.translate_percent is not None:\n            translate = {key: self.py_random.uniform(*value) for key, value in self.translate_percent.items()}\n            return cast(\n                fgeometric.XYInt,\n                {\"x\": int(translate[\"x\"] * width), \"y\": int(translate[\"y\"] * height)},\n            )\n        return cast(fgeometric.XYInt, {\"x\": 0, \"y\": 0})\n\n    def _get_shear_params(self) -> fgeometric.XYFloat:\n        return {\n            \"x\": -self.py_random.uniform(*self.shear[\"x\"]),\n            \"y\": -self.py_random.uniform(*self.shear[\"y\"]),\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.BaseDistortion","title":"class BaseDistortion (interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Base class for distortion-based transformations.

This class provides a foundation for implementing various types of image distortions, such as optical distortions, grid distortions, and elastic transformations. It handles the common operations of applying distortions to images, masks, bounding boxes, and keypoints.

Parameters:

Name Type Description interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR, cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This is an abstract base class and should not be used directly.
  • Subclasses should implement the get_params_dependent_on_data method to generate the distortion maps (map_x and map_y).
  • The distortion is applied consistently across all targets (image, mask, bboxes, keypoints) to maintain coherence in the augmented data.

Example of a subclass: class CustomDistortion(BaseDistortion): def init(self, args, **kwargs): super().init(args, **kwargs) # Add custom parameters here

    def get_params_dependent_on_data(self, params, data):\n        # Generate and return map_x and map_y based on the distortion logic\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self):\n        return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class BaseDistortion(DualTransform):\n    \"\"\"Base class for distortion-based transformations.\n\n    This class provides a foundation for implementing various types of image distortions,\n    such as optical distortions, grid distortions, and elastic transformations. It handles\n    the common operations of applying distortions to images, masks, bounding boxes, and keypoints.\n\n    Args:\n        interpolation (int): Interpolation method to be used for image transformation.\n            Should be one of the OpenCV interpolation types (e.g., cv2.INTER_LINEAR,\n            cv2.INTER_CUBIC). Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This is an abstract base class and should not be used directly.\n        - Subclasses should implement the `get_params_dependent_on_data` method to generate\n          the distortion maps (map_x and map_y).\n        - The distortion is applied consistently across all targets (image, mask, bboxes, keypoints)\n          to maintain coherence in the augmented data.\n\n    Example of a subclass:\n        class CustomDistortion(BaseDistortion):\n            def __init__(self, *args, **kwargs):\n                super().__init__(*args, **kwargs)\n                # Add custom parameters here\n\n            def get_params_dependent_on_data(self, params, data):\n                # Generate and return map_x and map_y based on the distortion logic\n                return {\"map_x\": map_x, \"map_y\": map_y}\n\n            def get_transform_init_args_names(self):\n                return super().get_transform_init_args_names() + (\"custom_param1\", \"custom_param2\")\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            img,\n            map_x,\n            map_y,\n            self.interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap(\n            mask,\n            map_x,\n            map_y,\n            self.mask_interpolation,\n            cv2.BORDER_CONSTANT,\n            0,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        bboxes_returned = fgeometric.remap_bboxes(\n            bboxes_denorm,\n            map_x,\n            map_y,\n            image_shape,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        map_x: np.ndarray,\n        map_y: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.remap_keypoints(keypoints, map_x, map_y, params[\"shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.D4","title":"class D4 (p=1, always_apply=None) [view source on GitHub]","text":"

Applies one of the eight possible D4 dihedral group transformations to a square-shaped input, maintaining the square shape. These transformations correspond to the symmetries of a square, including rotations and reflections.

The D4 group transformations include: - 'e' (identity): No transformation is applied. - 'r90' (rotation by 90 degrees counterclockwise) - 'r180' (rotation by 180 degrees) - 'r270' (rotation by 270 degrees counterclockwise) - 'v' (reflection across the vertical midline) - 'hvt' (reflection across the anti-diagonal) - 'h' (reflection across the horizontal midline) - 't' (reflection across the main diagonal)

Even if the probability (p) of applying the transform is set to 1, the identity transformation 'e' may still occur, which means the input will remain unchanged in one out of eight cases.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform is particularly useful for augmenting data that does not have a clear orientation, such as top-view satellite or drone imagery, or certain types of medical images.
  • The input image should be square-shaped for optimal results. Non-square inputs may lead to unexpected behavior or distortions.
  • When applied to bounding boxes or keypoints, their coordinates will be adjusted according to the selected transformation.
  • This transform preserves the aspect ratio and size of the input.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.D4(p=1.0),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed['image']\n# The resulting image will be one of the 8 possible D4 transformations of the input\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class D4(DualTransform):\n    \"\"\"Applies one of the eight possible D4 dihedral group transformations to a square-shaped input,\n    maintaining the square shape. These transformations correspond to the symmetries of a square,\n    including rotations and reflections.\n\n    The D4 group transformations include:\n    - 'e' (identity): No transformation is applied.\n    - 'r90' (rotation by 90 degrees counterclockwise)\n    - 'r180' (rotation by 180 degrees)\n    - 'r270' (rotation by 270 degrees counterclockwise)\n    - 'v' (reflection across the vertical midline)\n    - 'hvt' (reflection across the anti-diagonal)\n    - 'h' (reflection across the horizontal midline)\n    - 't' (reflection across the main diagonal)\n\n    Even if the probability (`p`) of applying the transform is set to 1, the identity transformation\n    'e' may still occur, which means the input will remain unchanged in one out of eight cases.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for augmenting data that does not have a clear orientation,\n          such as top-view satellite or drone imagery, or certain types of medical images.\n        - The input image should be square-shaped for optimal results. Non-square inputs may lead to\n          unexpected behavior or distortions.\n        - When applied to bounding boxes or keypoints, their coordinates will be adjusted according\n          to the selected transformation.\n        - This transform preserves the aspect ratio and size of the input.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.D4(p=1.0),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed['image']\n        # The resulting image will be one of the 8 possible D4 transformations of the input\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(\n        self,\n        p: float = 1,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def apply(\n        self,\n        img: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.d4(img, group_element)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.bboxes_d4(bboxes, group_element)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        group_element: D4Type,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.keypoints_d4(keypoints, group_element, params[\"shape\"])\n\n    def get_params(self) -> dict[str, D4Type]:\n        return {\n            \"group_element\": self.random_generator.choice(d4_group_elements),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ElasticTransform","title":"class ElasticTransform (alpha=1, sigma=50, interpolation=1, border_mode=4, value=None, mask_value=None, approximate=False, same_dxdy=False, mask_interpolation=0, noise_distribution='gaussian', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformation to images, masks, bounding boxes, and keypoints.

This transformation introduces random elastic distortions to the input data. It's particularly useful for data augmentation in training deep learning models, especially for tasks like image segmentation or object detection where you want to maintain the relative positions of features while introducing realistic deformations.

The transform works by generating random displacement fields and applying them to the input. These fields are smoothed using a Gaussian filter to create more natural-looking distortions.

Parameters:

Name Type Description alpha float

Scaling factor for the random displacement fields. Higher values result in more pronounced distortions. Default: 1.0

sigma float

Standard deviation of the Gaussian filter used to smooth the displacement fields. Higher values result in smoother, more global distortions. Default: 50.0

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

approximate bool

Whether to use an approximate version of the elastic transform. If True, uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially less accurate for large sigma values. Default: False

same_dxdy bool

Whether to use the same random displacement field for both x and y directions. Can speed up the transform at the cost of less diverse distortions. Default: False

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

noise_distribution Literal[\"gaussian\", \"uniform\"]

Distribution used to generate the displacement fields. \"gaussian\" generates fields using normal distribution (more natural deformations). \"uniform\" generates fields using uniform distribution (more mechanical deformations). Default: \"gaussian\".

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints) by using the same displacement fields for all.
  • The 'approximate' parameter determines whether to use a precise or approximate method for generating displacement fields. The approximate method can be faster but may be less accurate for large sigma values.
  • Bounding boxes that end up outside the image after transformation will be removed.
  • Keypoints that end up outside the image after transformation will be removed.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ElasticTransform(BaseDistortion):\n    \"\"\"Apply elastic deformation to images, masks, bounding boxes, and keypoints.\n\n    This transformation introduces random elastic distortions to the input data. It's particularly\n    useful for data augmentation in training deep learning models, especially for tasks like\n    image segmentation or object detection where you want to maintain the relative positions of\n    features while introducing realistic deformations.\n\n    The transform works by generating random displacement fields and applying them to the input.\n    These fields are smoothed using a Gaussian filter to create more natural-looking distortions.\n\n    Args:\n        alpha (float): Scaling factor for the random displacement fields. Higher values result in\n            more pronounced distortions. Default: 1.0\n        sigma (float): Standard deviation of the Gaussian filter used to smooth the displacement\n            fields. Higher values result in smoother, more global distortions. Default: 50.0\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        approximate (bool): Whether to use an approximate version of the elastic transform. If True,\n            uses a fixed kernel size for Gaussian smoothing, which can be faster but potentially\n            less accurate for large sigma values. Default: False\n        same_dxdy (bool): Whether to use the same random displacement field for both x and y\n            directions. Can speed up the transform at the cost of less diverse distortions. Default: False\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        noise_distribution (Literal[\"gaussian\", \"uniform\"]): Distribution used to generate the displacement fields.\n            \"gaussian\" generates fields using normal distribution (more natural deformations).\n            \"uniform\" generates fields using uniform distribution (more mechanical deformations).\n            Default: \"gaussian\".\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints)\n          by using the same displacement fields for all.\n        - The 'approximate' parameter determines whether to use a precise or approximate method for\n          generating displacement fields. The approximate method can be faster but may be less\n          accurate for large sigma values.\n        - Bounding boxes that end up outside the image after transformation will be removed.\n        - Keypoints that end up outside the image after transformation will be removed.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.ElasticTransform(alpha=1, sigma=50, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        alpha: Annotated[float, Field(ge=0)]\n        sigma: Annotated[float, Field(ge=1)]\n        approximate: bool\n        same_dxdy: bool\n        noise_distribution: Literal[\"gaussian\", \"uniform\"]\n        border_mode: BorderModeType = Field(deprecated=\"Deprecated\")\n        value: ColorType | None = Field(deprecated=\"Deprecated\")\n        mask_value: ColorType | None = Field(deprecated=\"Deprecated\")\n\n    def __init__(\n        self,\n        alpha: float = 1,\n        sigma: float = 50,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        approximate: bool = False,\n        same_dxdy: bool = False,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        noise_distribution: Literal[\"gaussian\", \"uniform\"] = \"gaussian\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.alpha = alpha\n        self.sigma = sigma\n        self.approximate = approximate\n        self.same_dxdy = same_dxdy\n        self.noise_distribution = noise_distribution\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n        kernel_size = (0, 0) if self.approximate else (17, 17)\n\n        # Generate displacement fields\n        dx, dy = fgeometric.generate_displacement_fields(\n            (height, width),\n            self.alpha,\n            self.sigma,\n            same_dxdy=self.same_dxdy,\n            kernel_size=kernel_size,\n            random_generator=self.random_generator,\n            noise_distribution=self.noise_distribution,\n        )\n\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        map_x = np.float32(x + dx)\n        map_y = np.float32(y + dy)\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"alpha\",\n            \"sigma\",\n            \"approximate\",\n            \"same_dxdy\",\n            \"noise_distribution\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.GridDistortion","title":"class GridDistortion (num_steps=5, distort_limit=(-0.3, 0.3), interpolation=1, border_mode=4, value=None, mask_value=None, normalized=True, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply grid distortion to images, masks, bounding boxes, and keypoints.

This transformation divides the image into a grid and randomly distorts each cell, creating localized warping effects. It's particularly useful for data augmentation in tasks like medical image analysis, OCR, and other domains where local geometric variations are meaningful.

Parameters:

Name Type Description num_steps int

Number of grid cells on each side of the image. Higher values create more granular distortions. Must be at least 1. Default: 5.

distort_limit float or tuple[float, float]

Range of distortion. If a single float is provided, the range will be (-distort_limit, distort_limit). Higher values create stronger distortions. Should be in the range of -1 to 1. Default: (-0.3, 0.3).

interpolation int

OpenCV interpolation method used for image transformation. Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.

normalized bool

If True, ensures that the distortion does not move pixels outside the image boundaries. This can result in less extreme distortions but guarantees that no information is lost. Default: True.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The same distortion is applied to all targets (image, mask, bboxes, keypoints) to maintain consistency.
  • When normalized=True, the distortion is adjusted to ensure all pixels remain within the image boundaries.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridDistortion(BaseDistortion):\n    \"\"\"Apply grid distortion to images, masks, bounding boxes, and keypoints.\n\n    This transformation divides the image into a grid and randomly distorts each cell,\n    creating localized warping effects. It's particularly useful for data augmentation\n    in tasks like medical image analysis, OCR, and other domains where local geometric\n    variations are meaningful.\n\n    Args:\n        num_steps (int): Number of grid cells on each side of the image. Higher values\n            create more granular distortions. Must be at least 1. Default: 5.\n        distort_limit (float or tuple[float, float]): Range of distortion. If a single float\n            is provided, the range will be (-distort_limit, distort_limit). Higher values\n            create stronger distortions. Should be in the range of -1 to 1.\n            Default: (-0.3, 0.3).\n        interpolation (int): OpenCV interpolation method used for image transformation.\n            Options include cv2.INTER_LINEAR, cv2.INTER_CUBIC, etc. Default: cv2.INTER_LINEAR.\n        normalized (bool): If True, ensures that the distortion does not move pixels\n            outside the image boundaries. This can result in less extreme distortions\n            but guarantees that no information is lost. Default: True.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The same distortion is applied to all targets (image, mask, bboxes, keypoints)\n          to maintain consistency.\n        - When normalized=True, the distortion is adjusted to ensure all pixels remain\n          within the image boundaries.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.GridDistortion(num_steps=5, distort_limit=0.3, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        num_steps: Annotated[int, Field(ge=1)]\n        distort_limit: SymmetricRangeType\n        normalized: bool\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int = Field(deprecated=\"Deprecated. Does not have any effect.\")\n\n        @field_validator(\"distort_limit\")\n        @classmethod\n        def check_limits(\n            cls,\n            v: tuple[float, float],\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = -1, 1\n            result = to_tuple(v)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        num_steps: int = 5,\n        distort_limit: ScaleFloatType = (-0.3, 0.3),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        normalized: bool = True,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.num_steps = num_steps\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.normalized = normalized\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        steps_x = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n        steps_y = [1 + self.py_random.uniform(*self.distort_limit) for _ in range(self.num_steps + 1)]\n\n        if self.normalized:\n            normalized_params = fgeometric.normalize_grid_distortion_steps(\n                image_shape,\n                self.num_steps,\n                steps_x,\n                steps_y,\n            )\n            steps_x, steps_y = (\n                normalized_params[\"steps_x\"],\n                normalized_params[\"steps_y\"],\n            )\n\n        map_x, map_y = fgeometric.generate_grid(\n            image_shape,\n            steps_x,\n            steps_y,\n            self.num_steps,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            *super().get_transform_init_args_names(),\n            \"num_steps\",\n            \"distort_limit\",\n            \"normalized\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.GridElasticDeform","title":"class GridElasticDeform (num_grid_xy, magnitude, interpolation=1, mask_interpolation=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.

This transformation overlays a grid on the input and applies random displacements to the grid points, resulting in local elastic distortions. The granularity and intensity of the distortions can be controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.

Parameters:

Name Type Description num_grid_xy tuple[int, int]

Number of grid cells along the width and height. Specified as (grid_width, grid_height). Each value must be greater than 1.

magnitude int

Maximum pixel-wise displacement for distortion. Must be greater than 0.

interpolation int

Interpolation method to be used for the image transformation. Default: cv2.INTER_LINEAR

mask_interpolation int

Interpolation method to be used for mask transformation. Default: cv2.INTER_NEAREST

p float

Probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Examples:

Python
>>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n>>> result = transform(image=image, mask=mask)\n>>> transformed_image, transformed_mask = result['image'], result['mask']\n

Note

This transformation is particularly useful for data augmentation in medical imaging and other domains where elastic deformations can simulate realistic variations.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class GridElasticDeform(DualTransform):\n    \"\"\"Apply elastic deformations to images, masks, bounding boxes, and keypoints using a grid-based approach.\n\n    This transformation overlays a grid on the input and applies random displacements to the grid points,\n    resulting in local elastic distortions. The granularity and intensity of the distortions can be\n    controlled using the dimensions of the overlaying distortion grid and the magnitude parameter.\n\n\n    Args:\n        num_grid_xy (tuple[int, int]): Number of grid cells along the width and height.\n            Specified as (grid_width, grid_height). Each value must be greater than 1.\n        magnitude (int): Maximum pixel-wise displacement for distortion. Must be greater than 0.\n        interpolation (int): Interpolation method to be used for the image transformation.\n            Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Interpolation method to be used for mask transformation.\n            Default: cv2.INTER_NEAREST\n        p (float): Probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Example:\n        >>> transform = GridElasticDeform(num_grid_xy=(4, 4), magnitude=10, p=1.0)\n        >>> result = transform(image=image, mask=mask)\n        >>> transformed_image, transformed_mask = result['image'], result['mask']\n\n    Note:\n        This transformation is particularly useful for data augmentation in medical imaging\n        and other domains where elastic deformations can simulate realistic variations.\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        num_grid_xy: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n        magnitude: int = Field(gt=0)\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n\n    def __init__(\n        self,\n        num_grid_xy: tuple[int, int],\n        magnitude: int,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_grid_xy = num_grid_xy\n        self.magnitude = magnitude\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    @staticmethod\n    def generate_mesh(polygons: np.ndarray, dimensions: np.ndarray) -> np.ndarray:\n        return np.hstack((dimensions.reshape(-1, 4), polygons))\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        # Replace calculate_grid_dimensions with split_uniform_grid\n        tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.num_grid_xy,\n            self.random_generator,\n        )\n\n        # Convert tiles to the format expected by generate_distorted_grid_polygons\n        dimensions = np.array(\n            [\n                [\n                    tile[1],\n                    tile[0],\n                    tile[3],\n                    tile[2],\n                ]  # Reorder to [x_min, y_min, x_max, y_max]\n                for tile in tiles\n            ],\n        ).reshape(\n            self.num_grid_xy[::-1] + (4,),\n        )  # Reshape to (grid_height, grid_width, 4)\n\n        polygons = fgeometric.generate_distorted_grid_polygons(\n            dimensions,\n            self.magnitude,\n            self.random_generator,\n        )\n\n        generated_mesh = self.generate_mesh(polygons, dimensions)\n\n        return {\"generated_mesh\": generated_mesh}\n\n    def apply(\n        self,\n        img: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(img, generated_mesh, self.interpolation)\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image(mask, generated_mesh, self.mask_interpolation)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        bboxes_denorm = denormalize_bboxes(bboxes, params[\"shape\"][:2])\n        return normalize_bboxes(\n            fgeometric.bbox_distort_image(\n                bboxes_denorm,\n                generated_mesh,\n                params[\"shape\"][:2],\n            ),\n            params[\"shape\"][:2],\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        generated_mesh: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.distort_image_keypoints(\n            keypoints,\n            generated_mesh,\n            params[\"shape\"][:2],\n        )\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"num_grid_xy\", \"magnitude\", \"interpolation\", \"mask_interpolation\"\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.HorizontalFlip","title":"class HorizontalFlip [view source on GitHub]","text":"

Flip the input horizontally around the y-axis.

Parameters:

Name Type Description p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class HorizontalFlip(DualTransform):\n    \"\"\"Flip the input horizontally around the y-axis.\n\n    Args:\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return hflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_hflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_hflip(keypoints, params[\"shape\"][1])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.OpticalDistortion","title":"class OpticalDistortion (distort_limit=(-0.05, 0.05), shift_limit=None, interpolation=1, border_mode=None, value=None, mask_value=None, mask_interpolation=0, mode='camera', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply optical distortion to images, masks, bounding boxes, and keypoints.

Supports two distortion models: 1. Camera matrix model (original): Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients

  1. Fisheye model: Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)

Parameters:

Name Type Description distort_limit float | tuple[float, float]

Range of distortion coefficient. For camera model: recommended range (-0.05, 0.05) For fisheye model: recommended range (-0.3, 0.3) Default: (-0.05, 0.05)

mode Literal['camera', 'fisheye']

Distortion model to use: - 'camera': Original camera matrix model - 'fisheye': Fisheye lens model Default: 'camera'

interpolation OpenCV flag

Interpolation method used for image transformation. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.
  • The distortion coefficient (k) is randomly sampled from the distort_limit range.
  • The image center is shifted by dx and dy, randomly sampled from the shift_limit range.
  • Bounding boxes and keypoints are transformed along with the image to maintain consistency.
  • Fisheye model directly applies radial distortion
  • Both models use shift_limit to control distortion center

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> transformed_image = transformed['image']\n>>> transformed_mask = transformed['mask']\n>>> transformed_bboxes = transformed['bboxes']\n>>> transformed_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class OpticalDistortion(BaseDistortion):\n    \"\"\"Apply optical distortion to images, masks, bounding boxes, and keypoints.\n\n    Supports two distortion models:\n    1. Camera matrix model (original):\n       Uses OpenCV's camera calibration model with k1=k2=k distortion coefficients\n\n    2. Fisheye model:\n       Direct radial distortion: r_dist = r * (1 + gamma * r\u00b2)\n\n    Args:\n        distort_limit (float | tuple[float, float]): Range of distortion coefficient.\n            For camera model: recommended range (-0.05, 0.05)\n            For fisheye model: recommended range (-0.3, 0.3)\n            Default: (-0.05, 0.05)\n\n        mode (Literal['camera', 'fisheye']): Distortion model to use:\n            - 'camera': Original camera matrix model\n            - 'fisheye': Fisheye lens model\n            Default: 'camera'\n\n        interpolation (OpenCV flag): Interpolation method used for image transformation.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC,\n            cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.\n\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The distortion is applied using OpenCV's initUndistortRectifyMap and remap functions.\n        - The distortion coefficient (k) is randomly sampled from the distort_limit range.\n        - The image center is shifted by dx and dy, randomly sampled from the shift_limit range.\n        - Bounding boxes and keypoints are transformed along with the image to maintain consistency.\n        - Fisheye model directly applies radial distortion\n        - Both models use shift_limit to control distortion center\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.OpticalDistortion(distort_limit=0.1, p=1.0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> transformed_image = transformed['image']\n        >>> transformed_mask = transformed['mask']\n        >>> transformed_bboxes = transformed['bboxes']\n        >>> transformed_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        distort_limit: SymmetricRangeType\n        mode: Literal[\"camera\", \"fisheye\"]\n        shift_limit: SymmetricRangeType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        mask_value: ColorType | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n        border_mode: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n    def __init__(\n        self,\n        distort_limit: ScaleFloatType = (-0.05, 0.05),\n        shift_limit: ScaleFloatType | None = None,\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int | None = None,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        mode: Literal[\"camera\", \"fisheye\"] = \"camera\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.distort_limit = cast(tuple[float, float], distort_limit)\n        self.mode = mode\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n        height, width = image_shape\n\n        # Get distortion coefficient\n        k = self.py_random.uniform(*self.distort_limit)\n\n        # Calculate center shift\n        center_xy = fgeometric.center(image_shape)\n\n        # Get distortion maps based on mode\n        if self.mode == \"camera\":\n            map_x, map_y = fgeometric.get_camera_matrix_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n        else:  # fisheye\n            map_x, map_y = fgeometric.get_fisheye_distortion_maps(\n                image_shape,\n                k,\n                center_xy,\n            )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"distort_limit\",\n            \"mode\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Pad","title":"class Pad (padding=0, fill=0, fill_mask=0, border_mode=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of an image by specified number of pixels.

Parameters:

Name Type Description padding int, tuple[int, int] or tuple[int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT

border_mode OpenCV flag

OpenCV border mode

p float

probability of applying the transform. Default: 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

References

  • https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Pad(DualTransform):\n    \"\"\"Pad the sides of an image by specified number of pixels.\n\n    Args:\n        padding (int, tuple[int, int] or tuple[int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int] - (pad_x, pad_y) to pad left/right by pad_x and top/bottom by pad_y\n            * tuple[int, int, int, int] - (left, top, right, bottom) specific padding per side\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT\n        fill_mask (ColorType): Padding value for mask if border_mode is cv2.BORDER_CONSTANT\n        border_mode (OpenCV flag): OpenCV border mode\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    References:\n        - https://pytorch.org/vision/main/generated/torchvision.transforms.v2.Pad.html\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        padding: int | tuple[int, int] | tuple[int, int, int, int]\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int] | tuple[int, int, int, int] = 0,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        border_mode: BorderModeType = cv2.BORDER_CONSTANT,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.border_mode = border_mode\n\n    def apply(\n        self,\n        img: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            img,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_with_params(\n            mask,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            border_mode=self.border_mode,\n            value=self.fill_mask,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_np = denormalize_bboxes(bboxes, params[\"shape\"])\n\n        result = fgeometric.pad_bboxes(\n            bboxes_np,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=image_shape,\n        )\n\n        rows, cols = params[\"shape\"][:2]\n        return normalize_bboxes(\n            result,\n            (rows + pad_top + pad_bottom, cols + pad_left + pad_right),\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        pad_top: int,\n        pad_bottom: int,\n        pad_left: int,\n        pad_right: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.pad_keypoints(\n            keypoints,\n            pad_top,\n            pad_bottom,\n            pad_left,\n            pad_right,\n            self.border_mode,\n            image_shape=params[\"shape\"][:2],\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        if isinstance(self.padding, Real):\n            pad_top = pad_bottom = pad_left = pad_right = self.padding\n        elif isinstance(self.padding, (tuple, list)):\n            if len(self.padding) == NUM_PADS_XY:\n                pad_left = pad_right = self.padding[0]\n                pad_top = pad_bottom = self.padding[1]\n            elif len(self.padding) == NUM_PADS_ALL_SIDES:\n                pad_left, pad_top, pad_right, pad_bottom = self.padding  # type: ignore[misc]\n            else:\n                raise TypeError(\n                    \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n                )\n        else:\n            raise TypeError(\n                \"Padding must be a single number, a pair of numbers, or a quadruple of numbers\",\n            )\n\n        return {\n            \"pad_top\": pad_top,\n            \"pad_bottom\": pad_bottom,\n            \"pad_left\": pad_left,\n            \"pad_right\": pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"padding\",\n            \"fill\",\n            \"fill_mask\",\n            \"border_mode\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.PadIfNeeded","title":"class PadIfNeeded (min_height=1024, min_width=1024, pad_height_divisor=None, pad_width_divisor=None, position='center', border_mode=4, value=None, mask_value=None, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of an image if the image dimensions are less than the specified minimum dimensions. If the pad_height_divisor or pad_width_divisor is specified, the function additionally ensures that the image dimensions are divisible by these values.

Parameters:

Name Type Description min_height int | None

Minimum desired height of the image. Ensures image height is at least this value. If not specified, pad_height_divisor must be provided.

min_width int | None

Minimum desired width of the image. Ensures image width is at least this value. If not specified, pad_width_divisor must be provided.

pad_height_divisor int | None

If set, pads the image height to make it divisible by this value. If not specified, min_height must be provided.

pad_width_divisor int | None

If set, pads the image width to make it divisible by this value. If not specified, min_width must be provided.

position Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]

Position where the image is to be placed after padding. Default is 'center'.

border_mode int

Specifies the border mode to use if padding is required. The default is cv2.BORDER_REFLECT_101.

fill ColorType | None

Value to fill the border pixels if the border mode is cv2.BORDER_CONSTANT. Default is None.

fill_mask ColorType | None

Similar to fill but used for padding masks. Default is None.

p float

Probability of applying the transform. Default is 1.0.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • Either min_height or pad_height_divisor must be set, but not both.
  • Either min_width or pad_width_divisor must be set, but not both.
  • If border_mode is set to cv2.BORDER_CONSTANT, value must be provided.
  • The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).
  • For bounding boxes, the coordinates will be adjusted to account for the padding.
  • For keypoints, their positions will be shifted according to the padding.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n... ])\n>>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n>>> padded_image = transformed['image']\n>>> padded_mask = transformed['mask']\n>>> adjusted_bboxes = transformed['bboxes']\n>>> adjusted_keypoints = transformed['keypoints']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PadIfNeeded(Pad):\n    \"\"\"Pads the sides of an image if the image dimensions are less than the specified minimum dimensions.\n    If the `pad_height_divisor` or `pad_width_divisor` is specified, the function additionally ensures\n    that the image dimensions are divisible by these values.\n\n    Args:\n        min_height (int | None): Minimum desired height of the image. Ensures image height is at least this value.\n            If not specified, pad_height_divisor must be provided.\n        min_width (int | None): Minimum desired width of the image. Ensures image width is at least this value.\n            If not specified, pad_width_divisor must be provided.\n        pad_height_divisor (int | None): If set, pads the image height to make it divisible by this value.\n            If not specified, min_height must be provided.\n        pad_width_divisor (int | None): If set, pads the image width to make it divisible by this value.\n            If not specified, min_width must be provided.\n        position (Literal[\"center\", \"top_left\", \"top_right\", \"bottom_left\", \"bottom_right\", \"random\"]):\n            Position where the image is to be placed after padding. Default is 'center'.\n        border_mode (int): Specifies the border mode to use if padding is required.\n            The default is `cv2.BORDER_REFLECT_101`.\n        fill (ColorType | None): Value to fill the border pixels if the border mode is `cv2.BORDER_CONSTANT`.\n            Default is None.\n        fill_mask (ColorType | None): Similar to `fill` but used for padding masks. Default is None.\n        p (float): Probability of applying the transform. Default is 1.0.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - Either `min_height` or `pad_height_divisor` must be set, but not both.\n        - Either `min_width` or `pad_width_divisor` must be set, but not both.\n        - If `border_mode` is set to `cv2.BORDER_CONSTANT`, `value` must be provided.\n        - The transform will maintain consistency across all targets (image, mask, bboxes, keypoints, volume).\n        - For bounding boxes, the coordinates will be adjusted to account for the padding.\n        - For keypoints, their positions will be shifted according to the padding.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.PadIfNeeded(min_height=1024, min_width=1024, border_mode=cv2.BORDER_CONSTANT, fill=0),\n        ... ])\n        >>> transformed = transform(image=image, mask=mask, bboxes=bboxes, keypoints=keypoints)\n        >>> padded_image = transformed['image']\n        >>> padded_mask = transformed['mask']\n        >>> adjusted_bboxes = transformed['bboxes']\n        >>> adjusted_keypoints = transformed['keypoints']\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        min_height: int | None = Field(ge=1)\n        min_width: int | None = Field(ge=1)\n        pad_height_divisor: int | None = Field(ge=1)\n        pad_width_divisor: int | None = Field(ge=1)\n        position: PositionType\n        border_mode: BorderModeType\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType\n        fill_mask: ColorType\n\n        @model_validator(mode=\"after\")\n        def validate_divisibility(self) -> Self:\n            if (self.min_height is None) == (self.pad_height_divisor is None):\n                msg = \"Only one of 'min_height' and 'pad_height_divisor' parameters must be set\"\n                raise ValueError(msg)\n            if (self.min_width is None) == (self.pad_width_divisor is None):\n                msg = \"Only one of 'min_width' and 'pad_width_divisor' parameters must be set\"\n                raise ValueError(msg)\n\n            if self.border_mode == cv2.BORDER_CONSTANT and self.fill is None:\n                msg = \"If 'border_mode' is set to 'BORDER_CONSTANT', 'fill' must be provided.\"\n                raise ValueError(msg)\n\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n\n            return self\n\n    def __init__(\n        self,\n        min_height: int | None = 1024,\n        min_width: int | None = 1024,\n        pad_height_divisor: int | None = None,\n        pad_width_divisor: int | None = None,\n        position: PositionType = \"center\",\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        # Initialize with dummy padding that will be calculated later\n        super().__init__(\n            padding=0,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            p=p,\n        )\n        self.min_height = min_height\n        self.min_width = min_width\n        self.pad_height_divisor = pad_height_divisor\n        self.pad_width_divisor = pad_width_divisor\n        self.position = position\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.get_padding_params(\n            image_shape=params[\"shape\"][:2],\n            min_height=self.min_height,\n            min_width=self.min_width,\n            pad_height_divisor=self.pad_height_divisor,\n            pad_width_divisor=self.pad_width_divisor,\n        )\n\n        h_pad_top, h_pad_bottom, w_pad_left, w_pad_right = fgeometric.adjust_padding_by_position(\n            h_top=h_pad_top,\n            h_bottom=h_pad_bottom,\n            w_left=w_pad_left,\n            w_right=w_pad_right,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\n            \"pad_top\": h_pad_top,\n            \"pad_bottom\": h_pad_bottom,\n            \"pad_left\": w_pad_left,\n            \"pad_right\": w_pad_right,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_height\",\n            \"min_width\",\n            \"pad_height_divisor\",\n            \"pad_width_divisor\",\n            \"position\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Perspective","title":"class Perspective (scale=(0.05, 0.1), keep_size=True, pad_mode=None, pad_val=None, mask_pad_val=None, fit_output=False, interpolation=1, mask_interpolation=0, border_mode=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply random four point perspective transformation to the input.

Parameters:

Name Type Description scale float or tuple of float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Default: (0.05, 0.1).

keep_size bool

Whether to resize image back to its original size after applying the perspective transform. If set to False, the resulting images may end up having different shapes. Default: True.

border_mode OpenCV flag

OpenCV border mode used for padding. Default: cv2.BORDER_CONSTANT.

fill ColorType

Padding value if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fill_mask ColorType

Padding value for mask if border_mode is cv2.BORDER_CONSTANT. Default: 0.

fit_output bool

If True, the image plane size and position will be adjusted to still capture the whole image after perspective transformation. This is followed by image resizing if keep_size is set to True. If False, parts of the transformed image may be outside of the image plane. This setting should not be set to True when using large scale values as it could lead to very large images. Default: False.

interpolation int

Interpolation method to be used for image transformation. Should be one of the OpenCV interpolation types. Default: cv2.INTER_LINEAR

mask_interpolation int

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

This transformation creates a perspective effect by randomly moving the four corners of the image. The amount of movement is controlled by the 'scale' parameter.

When 'keep_size' is True, the output image will have the same size as the input image, which may cause some parts of the transformed image to be cut off or padded.

When 'fit_output' is True, the transformation ensures that the entire transformed image is visible, which may result in a larger output image if keep_size is False.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n... ])\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Perspective(DualTransform):\n    \"\"\"Apply random four point perspective transformation to the input.\n\n    Args:\n        scale (float or tuple of float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Default: (0.05, 0.1).\n        keep_size (bool): Whether to resize image back to its original size after applying the perspective transform.\n            If set to False, the resulting images may end up having different shapes.\n            Default: True.\n        border_mode (OpenCV flag): OpenCV border mode used for padding.\n            Default: cv2.BORDER_CONSTANT.\n        fill (ColorType): Padding value if border_mode is cv2.BORDER_CONSTANT.\n            Default: 0.\n        fill_mask (ColorType): Padding value for mask if border_mode is\n            cv2.BORDER_CONSTANT. Default: 0.\n        fit_output (bool): If True, the image plane size and position will be adjusted to still capture\n            the whole image after perspective transformation. This is followed by image resizing if keep_size is set\n            to True. If False, parts of the transformed image may be outside of the image plane.\n            This setting should not be set to True when using large scale values as it could lead to very large images.\n            Default: False.\n        interpolation (int): Interpolation method to be used for image transformation. Should be one\n            of the OpenCV interpolation types. Default: cv2.INTER_LINEAR\n        mask_interpolation (int): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        This transformation creates a perspective effect by randomly moving the four corners of the image.\n        The amount of movement is controlled by the 'scale' parameter.\n\n        When 'keep_size' is True, the output image will have the same size as the input image,\n        which may cause some parts of the transformed image to be cut off or padded.\n\n        When 'fit_output' is True, the transformation ensures that the entire transformed image is visible,\n        which may result in a larger output image if keep_size is False.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.Perspective(scale=(0.05, 0.1), keep_size=True, always_apply=False, p=0.5),\n        ... ])\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        keep_size: bool\n        pad_mode: BorderModeType | None\n        pad_val: ColorType | None\n        mask_pad_val: ColorType | None\n        fit_output: bool\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        fill: ColorType\n        fill_mask: ColorType\n        border_mode: BorderModeType\n\n        @model_validator(mode=\"after\")\n        def validate_deprecated_fields(self) -> Self:\n            if self.pad_mode is not None:\n                warn(\"pad_mode is deprecated, use border_mode instead\", DeprecationWarning, stacklevel=2)\n                self.border_mode = self.pad_mode\n            if self.pad_val is not None:\n                warn(\"pad_val is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.pad_val\n            if self.mask_pad_val is not None:\n                warn(\"mask_pad_val is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_pad_val\n            return self\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.05, 0.1),\n        keep_size: bool = True,\n        pad_mode: int | None = None,\n        pad_val: ColorType | None = None,\n        mask_pad_val: ColorType | None = None,\n        fit_output: bool = False,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        border_mode: int = cv2.BORDER_CONSTANT,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p, always_apply=always_apply)\n        self.scale = cast(tuple[float, float], scale)\n        self.keep_size = keep_size\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.fit_output = fit_output\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n\n    def apply(\n        self,\n        img: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            img,\n            matrix,\n            max_width,\n            max_height,\n            self.fill,\n            self.border_mode,\n            self.keep_size,\n            self.interpolation,\n        )\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective(\n            mask,\n            matrix,\n            max_width,\n            max_height,\n            self.fill_mask,\n            self.border_mode,\n            self.keep_size,\n            self.mask_interpolation,\n        )\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        matrix_bbox: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_bboxes(\n            bboxes,\n            params[\"shape\"],\n            matrix_bbox,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        matrix: np.ndarray,\n        max_height: int,\n        max_width: int,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.perspective_keypoints(\n            keypoints,\n            params[\"shape\"],\n            matrix,\n            max_width,\n            max_height,\n            self.keep_size,\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        scale = self.py_random.uniform(*self.scale)\n\n        points = fgeometric.generate_perspective_points(\n            image_shape,\n            scale,\n            self.random_generator,\n        )\n        points = fgeometric.order_points(points)\n\n        matrix, max_width, max_height = fgeometric.compute_perspective_params(\n            points,\n            image_shape,\n        )\n\n        if self.fit_output:\n            matrix, max_width, max_height = fgeometric.expand_transform(\n                matrix,\n                image_shape,\n            )\n\n        return {\n            \"matrix\": matrix,\n            \"max_height\": max_height,\n            \"max_width\": max_width,\n            \"matrix_bbox\": matrix,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"keep_size\",\n            \"border_mode\",\n            \"fill\",\n            \"fill_mask\",\n            \"fit_output\",\n            \"interpolation\",\n            \"mask_interpolation\",\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.PiecewiseAffine","title":"class PiecewiseAffine (scale=(0.03, 0.05), nb_rows=(4, 4), nb_cols=(4, 4), interpolation=1, mask_interpolation=0, cval=None, cval_mask=None, mode=None, absolute_scale=False, p=0.5, always_apply=None, keypoints_threshold=0.01) [view source on GitHub]","text":"

Apply piecewise affine transformations to the input image.

This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points around via affine transformations. This leads to local distortions in the image.

Parameters:

Name Type Description scale tuple[float, float] | float

Standard deviation of the normal distributions. These are used to sample the random distances of the subimage's corners from the full image's corners. If scale is a single float value, the range will be (0, scale). Recommended values are in the range (0.01, 0.05) for small distortions, and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).

nb_rows tuple[int, int] | int

Number of rows of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of rows. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

nb_cols tuple[int, int] | int

Number of columns of points that the regular grid should have. Must be at least 2. For large images, you might want to pick a higher value than 4. If a single int, then that value will always be used as the number of columns. If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image. Default: 4.

interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

absolute_scale bool

If set to True, the value of the scale parameter will be treated as an absolute pixel value. If set to False, it will be treated as a fraction of the image height and width. Default: False.

p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This augmentation is very slow. Consider using ElasticTransform instead, which is at least 10x faster.
  • The augmentation may not always produce visible effects, especially with small scale values.
  • For keypoints and bounding boxes, the transformation might move them outside the image boundaries. In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n>>> transform = A.Compose([\n...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n... ])\n>>> transformed = transform(image=image)\n>>> transformed_image = transformed[\"image\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class PiecewiseAffine(BaseDistortion):\n    \"\"\"Apply piecewise affine transformations to the input image.\n\n    This augmentation places a regular grid of points on an image and randomly moves the neighborhood of these points\n    around via affine transformations. This leads to local distortions in the image.\n\n    Args:\n        scale (tuple[float, float] | float): Standard deviation of the normal distributions. These are used to sample\n            the random distances of the subimage's corners from the full image's corners.\n            If scale is a single float value, the range will be (0, scale).\n            Recommended values are in the range (0.01, 0.05) for small distortions,\n            and (0.05, 0.1) for larger distortions. Default: (0.03, 0.05).\n        nb_rows (tuple[int, int] | int): Number of rows of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of rows.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        nb_cols (tuple[int, int] | int): Number of columns of points that the regular grid should have.\n            Must be at least 2. For large images, you might want to pick a higher value than 4.\n            If a single int, then that value will always be used as the number of columns.\n            If a tuple (a, b), then a value from the discrete interval [a..b] will be uniformly sampled per image.\n            Default: 4.\n        interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        absolute_scale (bool): If set to True, the value of the scale parameter will be treated as an absolute\n            pixel value. If set to False, it will be treated as a fraction of the image height and width.\n            Default: False.\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This augmentation is very slow. Consider using `ElasticTransform` instead, which is at least 10x faster.\n        - The augmentation may not always produce visible effects, especially with small scale values.\n        - For keypoints and bounding boxes, the transformation might move them outside the image boundaries.\n          In such cases, the keypoints will be set to (-1, -1) and the bounding boxes will be removed.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)\n        >>> transform = A.Compose([\n        ...     A.PiecewiseAffine(scale=(0.03, 0.05), nb_rows=4, nb_cols=4, p=0.5),\n        ... ])\n        >>> transformed = transform(image=image)\n        >>> transformed_image = transformed[\"image\"]\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        scale: NonNegativeFloatRangeType\n        nb_rows: ScaleIntType\n        nb_cols: ScaleIntType\n        interpolation: InterpolationType\n        mask_interpolation: InterpolationType\n        cval: int | None = Field(deprecated=\"Deprecated. Does not have any effect.\")\n        cval_mask: int | None = Field(\n            deprecated=\"Deprecated. Does not have any effect.\",\n        )\n\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = Field(\n            deprecated=\"Deprecated. Does not have any effects.\",\n        )\n\n        absolute_scale: bool\n        keypoints_threshold: float = Field(\n            deprecated=\"This parameter is not used anymore\",\n        )\n\n        @field_validator(\"nb_rows\", \"nb_cols\")\n        @classmethod\n        def process_range(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> tuple[float, float]:\n            bounds = 2, BIG_INTEGER\n            result = to_tuple(value, value)\n            check_range(result, *bounds, info.field_name)\n            return result\n\n    def __init__(\n        self,\n        scale: ScaleFloatType = (0.03, 0.05),\n        nb_rows: ScaleIntType = (4, 4),\n        nb_cols: ScaleIntType = (4, 4),\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        cval: int | None = None,\n        cval_mask: int | None = None,\n        mode: Literal[\"constant\", \"edge\", \"symmetric\", \"reflect\", \"wrap\"] | None = None,\n        absolute_scale: bool = False,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n        keypoints_threshold: float = 0.01,\n    ):\n        super().__init__(\n            p=p,\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n        )\n\n        warn(\n            \"This augmenter is very slow. Try to use ``ElasticTransform`` instead, which is at least 10x faster.\",\n            stacklevel=2,\n        )\n\n        self.scale = cast(tuple[float, float], scale)\n        self.nb_rows = cast(tuple[int, int], nb_rows)\n        self.nb_cols = cast(tuple[int, int], nb_cols)\n        self.interpolation = interpolation\n        self.mask_interpolation = mask_interpolation\n        self.absolute_scale = absolute_scale\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale\",\n            \"nb_rows\",\n            \"nb_cols\",\n            \"interpolation\",\n            \"mask_interpolation\",\n            \"absolute_scale\",\n        )\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        image_shape = params[\"shape\"][:2]\n\n        nb_rows = np.clip(self.py_random.randint(*self.nb_rows), 2, None)\n        nb_cols = np.clip(self.py_random.randint(*self.nb_cols), 2, None)\n        scale = self.py_random.uniform(*self.scale)\n\n        map_x, map_y = fgeometric.create_piecewise_affine_maps(\n            image_shape=image_shape,\n            grid=(nb_rows, nb_cols),\n            scale=scale,\n            absolute_scale=self.absolute_scale,\n            random_generator=self.random_generator,\n        )\n\n        return {\"map_x\": map_x, \"map_y\": map_y}\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.RandomGridShuffle","title":"class RandomGridShuffle (grid=(3, 3), p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly shuffles the grid's cells on an image, mask, or keypoints, effectively rearranging patches within the image. This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.

Parameters:

Name Type Description grid tuple[int, int]

Size of the grid for splitting the image into cells. Each cell is shuffled randomly. For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled. Default: (3, 3)

p float

Probability that the transform will be applied. Should be in the range [0, 1]. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • This transform maintains consistency across all targets. If applied to an image and its corresponding mask or keypoints, the same shuffling will be applied to all.
  • The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2)) for the transform to have any effect.
  • Keypoints are moved along with their corresponding grid cell.
  • This transform could be useful when only micro features are important for the model, and memorizing the global structure could be harmful. For example:
  • Identifying the type of cell phone used to take a picture based on micro artifacts generated by phone post-processing algorithms, rather than the semantic features of the photo. See more at https://ieeexplore.ieee.org/abstract/document/8622031
  • Identifying stress, glucose, hydration levels based on skin images.

Mathematical Formulation: 1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter. 2. A random permutation P of integers from 0 to (mn - 1) is generated. 3. Each cell in the grid is assigned a number from 0 to (mn - 1) in row-major order. 4. The cells are then rearranged according to the permutation P.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [1, 1, 1, 2, 2, 2],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4],\n...     [3, 3, 3, 4, 4, 4]\n... ])\n>>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n>>> result = transform(image=image)\n>>> transformed_image = result['image']\n# The resulting image might look like this (one possible outcome):\n# [[4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [4, 4, 4, 2, 2, 2],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1],\n#  [3, 3, 3, 1, 1, 1]]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class RandomGridShuffle(DualTransform):\n    \"\"\"Randomly shuffles the grid's cells on an image, mask, or keypoints,\n    effectively rearranging patches within the image.\n    This transformation divides the image into a grid and then permutes these grid cells based on a random mapping.\n\n    Args:\n        grid (tuple[int, int]): Size of the grid for splitting the image into cells. Each cell is shuffled randomly.\n            For example, (3, 3) will divide the image into a 3x3 grid, resulting in 9 cells to be shuffled.\n            Default: (3, 3)\n        p (float): Probability that the transform will be applied. Should be in the range [0, 1].\n            Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform maintains consistency across all targets. If applied to an image and its corresponding\n          mask or keypoints, the same shuffling will be applied to all.\n        - The number of cells in the grid should be at least 2 (i.e., grid should be at least (1, 2), (2, 1), or (2, 2))\n          for the transform to have any effect.\n        - Keypoints are moved along with their corresponding grid cell.\n        - This transform could be useful when only micro features are important for the model, and memorizing\n          the global structure could be harmful. For example:\n          - Identifying the type of cell phone used to take a picture based on micro artifacts generated by\n            phone post-processing algorithms, rather than the semantic features of the photo.\n            See more at https://ieeexplore.ieee.org/abstract/document/8622031\n          - Identifying stress, glucose, hydration levels based on skin images.\n\n    Mathematical Formulation:\n        1. The image is divided into a grid of size (m, n) as specified by the 'grid' parameter.\n        2. A random permutation P of integers from 0 to (m*n - 1) is generated.\n        3. Each cell in the grid is assigned a number from 0 to (m*n - 1) in row-major order.\n        4. The cells are then rearranged according to the permutation P.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [1, 1, 1, 2, 2, 2],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4],\n        ...     [3, 3, 3, 4, 4, 4]\n        ... ])\n        >>> transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)\n        >>> result = transform(image=image)\n        >>> transformed_image = result['image']\n        # The resulting image might look like this (one possible outcome):\n        # [[4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [4, 4, 4, 2, 2, 2],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1],\n        #  [3, 3, 3, 1, 1, 1]]\n\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        grid: Annotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]\n\n    _targets = ALL_TARGETS\n\n    def __init__(\n        self,\n        grid: tuple[int, int] = (3, 3),\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.grid = grid\n\n    def apply(\n        self,\n        img: np.ndarray,\n        tiles: np.ndarray,\n        mapping: list[int],\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_image(img, tiles, mapping)\n\n    def apply_to_bboxes(\n        self,\n        bboxes: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        image_shape = params[\"shape\"][:2]\n        bboxes_denorm = denormalize_bboxes(bboxes, image_shape)\n        processor = cast(BboxProcessor, self.get_processor(\"bboxes\"))\n        if processor is None:\n            return bboxes\n        bboxes_returned = fgeometric.bboxes_grid_shuffle(\n            bboxes_denorm,\n            tiles,\n            mapping,\n            image_shape,\n            min_area=processor.params.min_area,\n            min_visibility=processor.params.min_visibility,\n        )\n        return normalize_bboxes(bboxes_returned, image_shape)\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        tiles: np.ndarray,\n        mapping: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        return fgeometric.swap_tiles_on_keypoints(keypoints, tiles, mapping)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, np.ndarray]:\n        image_shape = params[\"shape\"][:2]\n\n        original_tiles = fgeometric.split_uniform_grid(\n            image_shape,\n            self.grid,\n            self.random_generator,\n        )\n        shape_groups = fgeometric.create_shape_groups(original_tiles)\n        mapping = fgeometric.shuffle_tiles_within_shape_groups(\n            shape_groups,\n            self.random_generator,\n        )\n\n        return {\"tiles\": original_tiles, \"mapping\": mapping}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"grid\",)\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ShiftScaleRotate","title":"class ShiftScaleRotate (shift_limit=(-0.0625, 0.0625), scale_limit=(-0.1, 0.1), rotate_limit=(-45, 45), interpolation=1, border_mode=4, value=None, mask_value=None, shift_limit_x=None, shift_limit_y=None, rotate_method='largest_box', mask_interpolation=0, fill=0, fill_mask=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Randomly apply affine transforms: translate, scale and rotate the input.

Parameters:

Name Type Description shift_limit float, float) or float

shift factor range for both height and width. If shift_limit is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).

scale_limit float, float) or float

scaling factor range. If scale_limit is a single float value, the range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1. If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high). Default: (-0.1, 0.1).

rotate_limit int, int) or int

rotation range. If rotate_limit is a single int value, the range will be (-rotate_limit, rotate_limit). Default: (-45, 45).

interpolation OpenCV flag

flag that is used to specify the interpolation algorithm. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_LINEAR.

border_mode OpenCV flag

flag that is used to specify the pixel extrapolation method. Should be one of: cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101. Default: cv2.BORDER_REFLECT_101

fill ColorType

padding value if border_mode is cv2.BORDER_CONSTANT.

fill_mask ColorType

padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.

shift_limit_x float, float) or float

shift factor range for width. If it is set then this value instead of shift_limit will be used for shifting width. If shift_limit_x is a single float value, the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in the range [-1, 1]. Default: None.

shift_limit_y float, float) or float

shift factor range for height. If it is set then this value instead of shift_limit will be used for shifting height. If shift_limit_y is a single float value, the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie in the range [-, 1]. Default: None.

rotate_method str

rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\". Default: \"largest_box\"

mask_interpolation OpenCV flag

Flag that is used to specify the interpolation algorithm for mask. Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4. Default: cv2.INTER_NEAREST.

p float

probability of applying the transform. Default: 0.5.

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ShiftScaleRotate(Affine):\n    \"\"\"Randomly apply affine transforms: translate, scale and rotate the input.\n\n    Args:\n        shift_limit ((float, float) or float): shift factor range for both height and width. If shift_limit\n            is a single float value, the range will be (-shift_limit, shift_limit). Absolute values for lower and\n            upper bounds should lie in range [-1, 1]. Default: (-0.0625, 0.0625).\n        scale_limit ((float, float) or float): scaling factor range. If scale_limit is a single float value, the\n            range will be (-scale_limit, scale_limit). Note that the scale_limit will be biased by 1.\n            If scale_limit is a tuple, like (low, high), sampling will be done from the range (1 + low, 1 + high).\n            Default: (-0.1, 0.1).\n        rotate_limit ((int, int) or int): rotation range. If rotate_limit is a single int value, the\n            range will be (-rotate_limit, rotate_limit). Default: (-45, 45).\n        interpolation (OpenCV flag): flag that is used to specify the interpolation algorithm. Should be one of:\n            cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_LINEAR.\n        border_mode (OpenCV flag): flag that is used to specify the pixel extrapolation method. Should be one of:\n            cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101.\n            Default: cv2.BORDER_REFLECT_101\n        fill (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT.\n        fill_mask (ColorType): padding value if border_mode is cv2.BORDER_CONSTANT applied for masks.\n        shift_limit_x ((float, float) or float): shift factor range for width. If it is set then this value\n            instead of shift_limit will be used for shifting width.  If shift_limit_x is a single float value,\n            the range will be (-shift_limit_x, shift_limit_x). Absolute values for lower and upper bounds should lie in\n            the range [-1, 1]. Default: None.\n        shift_limit_y ((float, float) or float): shift factor range for height. If it is set then this value\n            instead of shift_limit will be used for shifting height.  If shift_limit_y is a single float value,\n            the range will be (-shift_limit_y, shift_limit_y). Absolute values for lower and upper bounds should lie\n            in the range [-, 1]. Default: None.\n        rotate_method (str): rotation method used for the bounding boxes. Should be one of \"largest_box\" or \"ellipse\".\n            Default: \"largest_box\"\n        mask_interpolation (OpenCV flag): Flag that is used to specify the interpolation algorithm for mask.\n            Should be one of: cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4.\n            Default: cv2.INTER_NEAREST.\n        p (float): probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    class InitSchema(BaseTransformInitSchema):\n        shift_limit: SymmetricRangeType\n        scale_limit: SymmetricRangeType\n        rotate_limit: SymmetricRangeType\n        interpolation: InterpolationType\n        border_mode: BorderModeType\n\n        value: ColorType | None\n        mask_value: ColorType | None\n\n        fill: ColorType = 0\n        fill_mask: ColorType = 0\n\n        shift_limit_x: ScaleFloatType | None\n        shift_limit_y: ScaleFloatType | None\n        rotate_method: Literal[\"largest_box\", \"ellipse\"]\n        mask_interpolation: InterpolationType\n\n        @model_validator(mode=\"after\")\n        def check_shift_limit(self) -> Self:\n            bounds = -1, 1\n            self.shift_limit_x = to_tuple(\n                self.shift_limit_x if self.shift_limit_x is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_x, *bounds, \"shift_limit_x\")\n            self.shift_limit_y = to_tuple(\n                self.shift_limit_y if self.shift_limit_y is not None else self.shift_limit,\n            )\n            check_range(self.shift_limit_y, *bounds, \"shift_limit_y\")\n\n            if self.value is not None:\n                warn(\"value is deprecated, use fill instead\", DeprecationWarning, stacklevel=2)\n                self.fill = self.value\n            if self.mask_value is not None:\n                warn(\"mask_value is deprecated, use fill_mask instead\", DeprecationWarning, stacklevel=2)\n                self.fill_mask = self.mask_value\n            return self\n\n        @field_validator(\"scale_limit\")\n        @classmethod\n        def check_scale_limit(\n            cls,\n            value: ScaleFloatType,\n            info: ValidationInfo,\n        ) -> ScaleFloatType:\n            bounds = 0, float(\"inf\")\n            result = to_tuple(value, bias=1.0)\n            check_range(result, *bounds, str(info.field_name))\n            return result\n\n    def __init__(\n        self,\n        shift_limit: ScaleFloatType = (-0.0625, 0.0625),\n        scale_limit: ScaleFloatType = (-0.1, 0.1),\n        rotate_limit: ScaleFloatType = (-45, 45),\n        interpolation: int = cv2.INTER_LINEAR,\n        border_mode: int = cv2.BORDER_REFLECT_101,\n        value: ColorType | None = None,\n        mask_value: ColorType | None = None,\n        shift_limit_x: ScaleFloatType | None = None,\n        shift_limit_y: ScaleFloatType | None = None,\n        rotate_method: Literal[\"largest_box\", \"ellipse\"] = \"largest_box\",\n        mask_interpolation: InterpolationType = cv2.INTER_NEAREST,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        shift_limit_x = cast(tuple[float, float], shift_limit_x)\n        shift_limit_y = cast(tuple[float, float], shift_limit_y)\n        super().__init__(\n            scale=scale_limit,\n            translate_percent={\"x\": shift_limit_x, \"y\": shift_limit_y},\n            rotate=rotate_limit,\n            shear=(0, 0),\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            fill=fill,\n            fill_mask=fill_mask,\n            border_mode=border_mode,\n            fit_output=False,\n            keep_ratio=False,\n            rotate_method=rotate_method,\n            always_apply=always_apply,\n            p=p,\n        )\n        warn(\n            \"ShiftScaleRotate is deprecated. Please use Affine transform instead.\",\n            DeprecationWarning,\n            stacklevel=2,\n        )\n        self.shift_limit_x = shift_limit_x\n        self.shift_limit_y = shift_limit_y\n\n        self.scale_limit = cast(tuple[float, float], scale_limit)\n        self.rotate_limit = cast(tuple[int, int], rotate_limit)\n        self.border_mode = border_mode\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        return {\n            \"shift_limit_x\": self.shift_limit_x,\n            \"shift_limit_y\": self.shift_limit_y,\n            \"scale_limit\": to_tuple(self.scale_limit, bias=-1.0),\n            \"rotate_limit\": self.rotate_limit,\n            \"interpolation\": self.interpolation,\n            \"border_mode\": self.border_mode,\n            \"fill\": self.fill,\n            \"fill_mask\": self.fill_mask,\n            \"rotate_method\": self.rotate_method,\n            \"mask_interpolation\": self.mask_interpolation,\n        }\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.ThinPlateSpline","title":"class ThinPlateSpline (scale_range=(0.2, 0.4), num_control_points=4, interpolation=1, mask_interpolation=0, p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.

Imagine the image printed on a thin metal plate that can be bent and warped smoothly: - Control points act like pins pushing or pulling the plate - The plate resists sharp bending, creating smooth deformations - The transformation maintains continuity (no tears or folds) - Areas between control points are interpolated naturally

The transform works by: 1. Creating a regular grid of control points (like pins in the plate) 2. Randomly displacing these points (like pushing/pulling the pins) 3. Computing a smooth interpolation (like the plate bending) 4. Applying the resulting deformation to the image

Parameters:

Name Type Description scale_range tuple[float, float]

Range for random displacement of control points. Values should be in [0.0, 1.0]: - 0.0: No displacement (identity transform) - 0.1: Subtle warping - 0.2-0.4: Moderate deformation (recommended range) - 0.5+: Strong warping Default: (0.2, 0.4)

num_control_points int

Number of control points per side. Creates a grid of num_control_points x num_control_points points. - 2: Minimal deformation (affine-like) - 3-4: Moderate flexibility (recommended) - 5+: More local deformation control Must be >= 2. Default: 4

interpolation int

OpenCV interpolation flag. Used for image sampling. See also: cv2.INTER_* Default: cv2.INTER_LINEAR

p float

Probability of applying the transform. Default: 0.5

Targets

image, mask, keypoints, bboxes, volume, mask3d

Image types: uint8, float32

Note

  • The transformation preserves smoothness and continuity
  • Stronger scale values may create more extreme deformations
  • Higher number of control points allows more local deformations
  • The same deformation is applied consistently to all targets

Examples:

Python
>>> import albumentations as A\n>>> # Basic usage\n>>> transform = A.ThinPlateSpline()\n>>>\n>>> # Subtle deformation\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.1, 0.2),\n...     num_control_points=3\n... )\n>>>\n>>> # Strong warping with fine control\n>>> transform = A.ThinPlateSpline(\n...     scale_range=(0.3, 0.5),\n...     num_control_points=5,\n... )\n

References

  • \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\" by F.L. Bookstein https://doi.org/10.1109/34.24792

  • Thin Plate Splines in Computer Vision: https://en.wikipedia.org/wiki/Thin_plate_spline

  • Similar implementation in Kornia: https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline

See Also: - ElasticTransform: For different type of non-rigid deformation - GridDistortion: For grid-based warping - OpticalDistortion: For lens-like distortions

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class ThinPlateSpline(BaseDistortion):\n    r\"\"\"Apply Thin Plate Spline (TPS) transformation to create smooth, non-rigid deformations.\n\n    Imagine the image printed on a thin metal plate that can be bent and warped smoothly:\n    - Control points act like pins pushing or pulling the plate\n    - The plate resists sharp bending, creating smooth deformations\n    - The transformation maintains continuity (no tears or folds)\n    - Areas between control points are interpolated naturally\n\n    The transform works by:\n    1. Creating a regular grid of control points (like pins in the plate)\n    2. Randomly displacing these points (like pushing/pulling the pins)\n    3. Computing a smooth interpolation (like the plate bending)\n    4. Applying the resulting deformation to the image\n\n\n    Args:\n        scale_range (tuple[float, float]): Range for random displacement of control points.\n            Values should be in [0.0, 1.0]:\n            - 0.0: No displacement (identity transform)\n            - 0.1: Subtle warping\n            - 0.2-0.4: Moderate deformation (recommended range)\n            - 0.5+: Strong warping\n            Default: (0.2, 0.4)\n\n        num_control_points (int): Number of control points per side.\n            Creates a grid of num_control_points x num_control_points points.\n            - 2: Minimal deformation (affine-like)\n            - 3-4: Moderate flexibility (recommended)\n            - 5+: More local deformation control\n            Must be >= 2. Default: 4\n\n        interpolation (int): OpenCV interpolation flag. Used for image sampling.\n            See also: cv2.INTER_*\n            Default: cv2.INTER_LINEAR\n\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        image, mask, keypoints, bboxes, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The transformation preserves smoothness and continuity\n        - Stronger scale values may create more extreme deformations\n        - Higher number of control points allows more local deformations\n        - The same deformation is applied consistently to all targets\n\n    Example:\n        >>> import albumentations as A\n        >>> # Basic usage\n        >>> transform = A.ThinPlateSpline()\n        >>>\n        >>> # Subtle deformation\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.1, 0.2),\n        ...     num_control_points=3\n        ... )\n        >>>\n        >>> # Strong warping with fine control\n        >>> transform = A.ThinPlateSpline(\n        ...     scale_range=(0.3, 0.5),\n        ...     num_control_points=5,\n        ... )\n\n    References:\n        - \"Principal Warps: Thin-Plate Splines and the Decomposition of Deformations\"\n          by F.L. Bookstein\n          https://doi.org/10.1109/34.24792\n\n        - Thin Plate Splines in Computer Vision:\n          https://en.wikipedia.org/wiki/Thin_plate_spline\n\n        - Similar implementation in Kornia:\n          https://kornia.readthedocs.io/en/latest/augmentation.html#kornia.augmentation.RandomThinPlateSpline\n\n    See Also:\n        - ElasticTransform: For different type of non-rigid deformation\n        - GridDistortion: For grid-based warping\n        - OpticalDistortion: For lens-like distortions\n    \"\"\"\n\n    class InitSchema(BaseDistortion.InitSchema):\n        scale_range: Annotated[tuple[float, float], AfterValidator(check_range_bounds(0, 1))]\n        num_control_points: int = Field(ge=2)\n\n    def __init__(\n        self,\n        scale_range: tuple[float, float] = (0.2, 0.4),\n        num_control_points: int = 4,\n        interpolation: int = cv2.INTER_LINEAR,\n        mask_interpolation: int = cv2.INTER_NEAREST,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            interpolation=interpolation,\n            mask_interpolation=mask_interpolation,\n            p=p,\n        )\n        self.scale_range = scale_range\n        self.num_control_points = num_control_points\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        height, width = params[\"shape\"][:2]\n\n        # Create regular grid of control points\n        grid_size = self.num_control_points\n        x = np.linspace(0, 1, grid_size)\n        y = np.linspace(0, 1, grid_size)\n        src_points = np.stack(np.meshgrid(x, y), axis=-1).reshape(-1, 2)\n\n        # Add random displacement to destination points\n        scale = self.py_random.uniform(*self.scale_range) / 10\n        dst_points = src_points + self.random_generator.normal(\n            0,\n            scale,\n            src_points.shape,\n        )\n\n        # Compute TPS weights\n        weights, affine = fgeometric.compute_tps_weights(src_points, dst_points)\n\n        # Create grid of points\n        x, y = np.meshgrid(np.arange(width), np.arange(height))\n        points = np.stack([x.flatten(), y.flatten()], axis=1).astype(np.float32)\n\n        # Transform points\n        transformed = fgeometric.tps_transform(\n            points / [width, height],\n            src_points,\n            weights,\n            affine,\n        )\n        transformed *= [width, height]\n\n        return {\n            \"map_x\": transformed[:, 0].reshape(height, width).astype(np.float32),\n            \"map_y\": transformed[:, 1].reshape(height, width).astype(np.float32),\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"scale_range\",\n            \"num_control_points\",\n            *super().get_transform_init_args_names(),\n        )\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.Transpose","title":"class Transpose [view source on GitHub]","text":"

Transpose the input by swapping its rows and columns.

This transform flips the image over its main diagonal, effectively switching its width and height. It's equivalent to a 90-degree rotation followed by a horizontal flip.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • The dimensions of the output will be swapped compared to the input. For example, an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).
  • This transform is its own inverse. Applying it twice will return the original input.
  • For multi-channel images (like RGB), the channels are preserved in their original order.
  • Bounding boxes will have their coordinates adjusted to match the new image dimensions.
  • Keypoints will have their x and y coordinates swapped.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (y_min, x_min, y_max, x_max) 3. For keypoints with coordinates (x, y): new_keypoint = (y, x)

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.Transpose(p=1.0)\n>>> result = transform(image=image)\n>>> transposed_image = result['image']\n>>> print(transposed_image)\n[[[ 1  2  3]\n  [ 7  8  9]]\n [[ 4  5  6]\n  [10 11 12]]]\n# The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class Transpose(DualTransform):\n    \"\"\"Transpose the input by swapping its rows and columns.\n\n    This transform flips the image over its main diagonal, effectively switching its width and height.\n    It's equivalent to a 90-degree rotation followed by a horizontal flip.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The dimensions of the output will be swapped compared to the input. For example,\n          an input image of shape (100, 200, 3) will result in an output of shape (200, 100, 3).\n        - This transform is its own inverse. Applying it twice will return the original input.\n        - For multi-channel images (like RGB), the channels are preserved in their original order.\n        - Bounding boxes will have their coordinates adjusted to match the new image dimensions.\n        - Keypoints will have their x and y coordinates swapped.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[j, i, k] for all i in [0, W-1], j in [0, H-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (y_min, x_min, y_max, x_max)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (y, x)\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.Transpose(p=1.0)\n        >>> result = transform(image=image)\n        >>> transposed_image = result['image']\n        >>> print(transposed_image)\n        [[[ 1  2  3]\n          [ 7  8  9]]\n         [[ 4  5  6]\n          [10 11 12]]]\n        # The original 2x2x3 image is now 2x2x3, with rows and columns swapped\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.transpose(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_transpose(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_transpose(keypoints)\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/geometric/transforms/#albumentations.augmentations.geometric.transforms.VerticalFlip","title":"class VerticalFlip [view source on GitHub]","text":"

Flip the input vertically around the x-axis.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 0.5.

Targets

image, mask, bboxes, keypoints, volume, mask3d

Image types: uint8, float32

Note

  • This transform flips the image upside down. The top of the image becomes the bottom and vice versa.
  • The dimensions of the image remain unchanged.
  • For multi-channel images (like RGB), each channel is flipped independently.
  • Bounding boxes are adjusted to match their new positions in the flipped image.
  • Keypoints are moved to their new positions in the flipped image.

Mathematical Details: 1. For an input image I of shape (H, W, C), the output O is: O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1] 2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max): new_bbox = (x_min, H-y_max, x_max, H-y_min) 3. For keypoints with coordinates (x, y): new_keypoint = (x, H-y) where H is the height of the image.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> image = np.array([\n...     [[1, 2, 3], [4, 5, 6]],\n...     [[7, 8, 9], [10, 11, 12]]\n... ])\n>>> transform = A.VerticalFlip(p=1.0)\n>>> result = transform(image=image)\n>>> flipped_image = result['image']\n>>> print(flipped_image)\n[[[ 7  8  9]\n  [10 11 12]]\n [[ 1  2  3]\n  [ 4  5  6]]]\n# The original image is flipped vertically, with rows reversed\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/geometric/transforms.py Python
class VerticalFlip(DualTransform):\n    \"\"\"Flip the input vertically around the x-axis.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 0.5.\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform flips the image upside down. The top of the image becomes the bottom and vice versa.\n        - The dimensions of the image remain unchanged.\n        - For multi-channel images (like RGB), each channel is flipped independently.\n        - Bounding boxes are adjusted to match their new positions in the flipped image.\n        - Keypoints are moved to their new positions in the flipped image.\n\n    Mathematical Details:\n        1. For an input image I of shape (H, W, C), the output O is:\n           O[i, j, k] = I[H-1-i, j, k] for all i in [0, H-1], j in [0, W-1], k in [0, C-1]\n        2. For bounding boxes with coordinates (x_min, y_min, x_max, y_max):\n           new_bbox = (x_min, H-y_max, x_max, H-y_min)\n        3. For keypoints with coordinates (x, y):\n           new_keypoint = (x, H-y)\n        where H is the height of the image.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> image = np.array([\n        ...     [[1, 2, 3], [4, 5, 6]],\n        ...     [[7, 8, 9], [10, 11, 12]]\n        ... ])\n        >>> transform = A.VerticalFlip(p=1.0)\n        >>> result = transform(image=image)\n        >>> flipped_image = result['image']\n        >>> print(flipped_image)\n        [[[ 7  8  9]\n          [10 11 12]]\n         [[ 1  2  3]\n          [ 4  5  6]]]\n        # The original image is flipped vertically, with rows reversed\n\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return vflip(img)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.bboxes_vflip(bboxes)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return fgeometric.keypoints_vflip(keypoints, params[\"shape\"][0])\n\n    def get_transform_init_args_names(self) -> tuple[()]:\n        return ()\n
"},{"location":"api_reference/augmentations/mixing/","title":"Index","text":"
  • Mixing transforms (augmentations.mixing.transforms)
  • Mixing functional transforms (albumentations.augmentations.mixing.functional)
"},{"location":"api_reference/augmentations/mixing/functional/","title":"Mixing transforms (augmentations.mixing.functional)","text":""},{"location":"api_reference/augmentations/mixing/transforms/","title":"Mixing transforms (augmentations.mixing.transforms)","text":""},{"location":"api_reference/augmentations/mixing/transforms/#albumentations.augmentations.mixing.transforms.OverlayElements","title":"class OverlayElements (metadata_key='overlay_metadata', p=0.5, always_apply=None) [view source on GitHub]","text":"

Apply overlay elements such as images and masks onto an input image. This transformation can be used to add various objects (e.g., stickers, logos) to images with optional masks and bounding boxes for better placement control.

Parameters:

Name Type Description metadata_key str

Additional target key for metadata. Default overlay_metadata.

p float

Probability of applying the transformation. Default: 0.5.

Possible Metadata Fields: - image (np.ndarray): The overlay image to be applied. This is a required field. - bbox (list[int]): The bounding box specifying the region where the overlay should be applied. It should contain four floats: [y_min, x_min, y_max, x_max]. If label_id is provided, it should be appended as the fifth element in the bbox. BBox should be in Albumentations format, that is the same as normalized Pascal VOC format [x_min / width, y_min / height, x_max / width, y_max / height] - mask (np.ndarray): An optional mask that defines the non-rectangular region of the overlay image. If not provided, the entire overlay image is used. - mask_id (int): An optional identifier for the mask. If provided, the regions specified by the mask will be labeled with this identifier in the output mask.

Targets

image, mask

Image types: uint8, float32

Reference

https://github.com/danaaubakirova/doc-augmentation

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/mixing/transforms.py Python
class OverlayElements(DualTransform):\n    \"\"\"Apply overlay elements such as images and masks onto an input image. This transformation can be used to add\n    various objects (e.g., stickers, logos) to images with optional masks and bounding boxes for better placement\n    control.\n\n    Args:\n        metadata_key (str): Additional target key for metadata. Default `overlay_metadata`.\n        p (float): Probability of applying the transformation. Default: 0.5.\n\n    Possible Metadata Fields:\n        - image (np.ndarray): The overlay image to be applied. This is a required field.\n        - bbox (list[int]): The bounding box specifying the region where the overlay should be applied. It should\n                            contain four floats: [y_min, x_min, y_max, x_max]. If `label_id` is provided, it should\n                            be appended as the fifth element in the bbox. BBox should be in Albumentations format,\n                            that is the same as normalized Pascal VOC format\n                            [x_min / width, y_min / height, x_max / width, y_max / height]\n        - mask (np.ndarray): An optional mask that defines the non-rectangular region of the overlay image. If not\n                             provided, the entire overlay image is used.\n        - mask_id (int): An optional identifier for the mask. If provided, the regions specified by the mask will\n                         be labeled with this identifier in the output mask.\n\n    Targets:\n        image, mask\n\n    Image types:\n        uint8, float32\n\n    Reference:\n        https://github.com/danaaubakirova/doc-augmentation\n\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    class InitSchema(BaseTransformInitSchema):\n        metadata_key: str\n\n    def __init__(\n        self,\n        metadata_key: str = \"overlay_metadata\",\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.metadata_key = metadata_key\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        return [self.metadata_key]\n\n    @staticmethod\n    def preprocess_metadata(\n        metadata: dict[str, Any],\n        img_shape: tuple[int, int],\n        random_state: random.Random,\n    ) -> dict[str, Any]:\n        overlay_image = metadata[\"image\"]\n        overlay_height, overlay_width = overlay_image.shape[:2]\n        image_height, image_width = img_shape[:2]\n\n        if \"bbox\" in metadata:\n            bbox = metadata[\"bbox\"]\n            bbox_np = np.array([bbox])\n            check_bboxes(bbox_np)\n            denormalized_bbox = denormalize_bboxes(bbox_np, img_shape[:2])[0]\n\n            x_min, y_min, x_max, y_max = (int(x) for x in denormalized_bbox[:4])\n\n            if \"mask\" in metadata:\n                mask = metadata[\"mask\"]\n                mask = cv2.resize(mask, (x_max - x_min, y_max - y_min), interpolation=cv2.INTER_NEAREST)\n            else:\n                mask = np.ones((y_max - y_min, x_max - x_min), dtype=np.uint8)\n\n            overlay_image = cv2.resize(overlay_image, (x_max - x_min, y_max - y_min), interpolation=cv2.INTER_AREA)\n            offset = (y_min, x_min)\n\n            if len(bbox) == LENGTH_RAW_BBOX and \"bbox_id\" in metadata:\n                bbox = [x_min, y_min, x_max, y_max, metadata[\"bbox_id\"]]\n            else:\n                bbox = (x_min, y_min, x_max, y_max, *bbox[4:])\n        else:\n            if image_height < overlay_height or image_width < overlay_width:\n                overlay_image = cv2.resize(overlay_image, (image_width, image_height), interpolation=cv2.INTER_AREA)\n                overlay_height, overlay_width = overlay_image.shape[:2]\n\n            mask = metadata[\"mask\"] if \"mask\" in metadata else np.ones_like(overlay_image, dtype=np.uint8)\n\n            max_x_offset = image_width - overlay_width\n            max_y_offset = image_height - overlay_height\n\n            offset_x = random_state.randint(0, max_x_offset)\n            offset_y = random_state.randint(0, max_y_offset)\n\n            offset = (offset_y, offset_x)\n\n            bbox = [\n                offset_x,\n                offset_y,\n                offset_x + overlay_width,\n                offset_y + overlay_height,\n            ]\n\n            if \"bbox_id\" in metadata:\n                bbox = [*bbox, metadata[\"bbox_id\"]]\n\n        result = {\n            \"overlay_image\": overlay_image,\n            \"overlay_mask\": mask,\n            \"offset\": offset,\n            \"bbox\": bbox,\n        }\n\n        if \"mask_id\" in metadata:\n            result[\"mask_id\"] = metadata[\"mask_id\"]\n\n        return result\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        metadata = data[self.metadata_key]\n        img_shape = params[\"shape\"]\n\n        if isinstance(metadata, list):\n            overlay_data = [self.preprocess_metadata(md, img_shape, self.py_random) for md in metadata]\n        else:\n            overlay_data = [self.preprocess_metadata(metadata, img_shape, self.py_random)]\n\n        return {\n            \"overlay_data\": overlay_data,\n        }\n\n    def apply(\n        self,\n        img: np.ndarray,\n        overlay_data: list[dict[str, Any]],\n        **params: Any,\n    ) -> np.ndarray:\n        for data in overlay_data:\n            overlay_image = data[\"overlay_image\"]\n            overlay_mask = data[\"overlay_mask\"]\n            offset = data[\"offset\"]\n            img = fmixing.copy_and_paste_blend(img, overlay_image, overlay_mask, offset=offset)\n        return img\n\n    def apply_to_mask(\n        self,\n        mask: np.ndarray,\n        overlay_data: list[dict[str, Any]],\n        **params: Any,\n    ) -> np.ndarray:\n        for data in overlay_data:\n            if \"mask_id\" in data and data[\"mask_id\"] is not None:\n                overlay_mask = data[\"overlay_mask\"]\n                offset = data[\"offset\"]\n                mask_id = data[\"mask_id\"]\n\n                y_min, x_min = offset\n                y_max = y_min + overlay_mask.shape[0]\n                x_max = x_min + overlay_mask.shape[1]\n\n                mask_section = mask[y_min:y_max, x_min:x_max]\n                mask_section[overlay_mask > 0] = mask_id\n\n        return mask\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"metadata_key\",)\n
"},{"location":"api_reference/augmentations/transforms3d/","title":"Index","text":"
  • 3D (Volumetric) transforms (augmentations.transforms3d.transforms)
  • 3D (Volumetric) functional transforms (albumentations.augmentations.transforms3d.functional)
"},{"location":"api_reference/augmentations/transforms3d/functional/","title":"3D (Volumetric) functional transforms (augmentations.transforms3d.functional)","text":""},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.adjust_padding_by_position3d","title":"def adjust_padding_by_position3d (paddings, position, py_random) [view source on GitHub]","text":"

Adjust padding values based on desired position for 3D data.

Parameters:

Name Type Description paddings list[tuple[int, int]]

List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]

position Literal['center', 'random']

Position of the image after padding. Either 'center' or 'random'

py_random Random

Random number generator

Returns:

Type Description tuple[int, int, int, int, int, int]

Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)

Source code in albumentations/augmentations/transforms3d/functional.py Python
def adjust_padding_by_position3d(\n    paddings: list[tuple[int, int]],  # [(front, back), (top, bottom), (left, right)]\n    position: Literal[\"center\", \"random\"],\n    py_random: random.Random,\n) -> tuple[int, int, int, int, int, int]:\n    \"\"\"Adjust padding values based on desired position for 3D data.\n\n    Args:\n        paddings: List of tuples containing padding pairs for each dimension [(d_pad), (h_pad), (w_pad)]\n        position: Position of the image after padding. Either 'center' or 'random'\n        py_random: Random number generator\n\n    Returns:\n        tuple[int, int, int, int, int, int]: Final padding values (d_front, d_back, h_top, h_bottom, w_left, w_right)\n    \"\"\"\n    if position == \"center\":\n        return (\n            paddings[0][0],  # d_front\n            paddings[0][1],  # d_back\n            paddings[1][0],  # h_top\n            paddings[1][1],  # h_bottom\n            paddings[2][0],  # w_left\n            paddings[2][1],  # w_right\n        )\n\n    # For random position, redistribute padding for each dimension\n    d_pad = sum(paddings[0])\n    h_pad = sum(paddings[1])\n    w_pad = sum(paddings[2])\n\n    return (\n        py_random.randint(0, d_pad),  # d_front\n        d_pad - py_random.randint(0, d_pad),  # d_back\n        py_random.randint(0, h_pad),  # h_top\n        h_pad - py_random.randint(0, h_pad),  # h_bottom\n        py_random.randint(0, w_pad),  # w_left\n        w_pad - py_random.randint(0, w_pad),  # w_right\n    )\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.crop3d","title":"def crop3d (volume, crop_coords) [view source on GitHub]","text":"

Crop 3D volume using coordinates.

Parameters:

Name Type Description volume ndarray

Input volume with shape (z, y, x) or (z, y, x, channels)

crop_coords tuple[int, int, int, int, int, int]

Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping

Returns:

Type Description ndarray

Cropped volume with same number of dimensions as input

Source code in albumentations/augmentations/transforms3d/functional.py Python
def crop3d(\n    volume: np.ndarray,\n    crop_coords: tuple[int, int, int, int, int, int],\n) -> np.ndarray:\n    \"\"\"Crop 3D volume using coordinates.\n\n    Args:\n        volume: Input volume with shape (z, y, x) or (z, y, x, channels)\n        crop_coords: Tuple of (z_min, z_max, y_min, y_max, x_min, x_max) coordinates for cropping\n\n    Returns:\n        Cropped volume with same number of dimensions as input\n    \"\"\"\n    z_min, z_max, y_min, y_max, x_min, x_max = crop_coords\n\n    return volume[z_min:z_max, y_min:y_max, x_min:x_max]\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.cutout3d","title":"def cutout3d (volume, holes, fill_value) [view source on GitHub]","text":"

Cut out holes in 3D volume and fill them with a given value.

Source code in albumentations/augmentations/transforms3d/functional.py Python
def cutout3d(volume: np.ndarray, holes: np.ndarray, fill_value: ColorType) -> np.ndarray:\n    \"\"\"Cut out holes in 3D volume and fill them with a given value.\"\"\"\n    volume = volume.copy()\n    for z1, y1, x1, z2, y2, x2 in holes:\n        volume[z1:z2, y1:y2, x1:x2] = fill_value\n    return volume\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.filter_keypoints_in_holes3d","title":"def filter_keypoints_in_holes3d (keypoints, holes) [view source on GitHub]","text":"

Filter out keypoints that are inside any of the 3D holes.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (num_keypoints, 3+). The first three columns are x, y, z coordinates.

holes np.ndarray

Array of holes with shape (num_holes, 6). Each hole is represented as [z1, y1, x1, z2, y2, x2].

Returns:

Type Description np.ndarray

Array of keypoints that are not inside any hole.

Source code in albumentations/augmentations/transforms3d/functional.py Python
@handle_empty_array(\"keypoints\")\ndef filter_keypoints_in_holes3d(keypoints: np.ndarray, holes: np.ndarray) -> np.ndarray:\n    \"\"\"Filter out keypoints that are inside any of the 3D holes.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (num_keypoints, 3+).\n                               The first three columns are x, y, z coordinates.\n        holes (np.ndarray): Array of holes with shape (num_holes, 6).\n                           Each hole is represented as [z1, y1, x1, z2, y2, x2].\n\n    Returns:\n        np.ndarray: Array of keypoints that are not inside any hole.\n    \"\"\"\n    if holes.size == 0:\n        return keypoints\n\n    # Broadcast keypoints and holes for vectorized comparison\n    # Convert keypoints from XYZ to ZYX for comparison with holes\n    kp_z = keypoints[:, 2][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_y = keypoints[:, 1][:, np.newaxis]  # Shape: (num_keypoints, 1)\n    kp_x = keypoints[:, 0][:, np.newaxis]  # Shape: (num_keypoints, 1)\n\n    # Extract hole coordinates (in ZYX order)\n    hole_z1 = holes[:, 0]  # Shape: (num_holes,)\n    hole_y1 = holes[:, 1]\n    hole_x1 = holes[:, 2]\n    hole_z2 = holes[:, 3]\n    hole_y2 = holes[:, 4]\n    hole_x2 = holes[:, 5]\n\n    # Check if each keypoint is inside each hole\n    inside_hole = (\n        (kp_z >= hole_z1)\n        & (kp_z < hole_z2)\n        & (kp_y >= hole_y1)\n        & (kp_y < hole_y2)\n        & (kp_x >= hole_x1)\n        & (kp_x < hole_x2)\n    )\n\n    # A keypoint is valid if it's not inside any hole\n    valid_keypoints = ~np.any(inside_hole, axis=1)\n\n    # Return filtered keypoints with same dtype as input\n    result = keypoints[valid_keypoints]\n    if len(result) == 0:\n        # Ensure empty result has correct shape and dtype\n        return np.array([], dtype=keypoints.dtype).reshape(0, keypoints.shape[1])\n    return result\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.pad_3d_with_params","title":"def pad_3d_with_params (volume, padding, value) [view source on GitHub]","text":"

Pad 3D volume with given parameters.

Parameters:

Name Type Description volume ndarray

Input volume with shape (depth, height, width) or (depth, height, width, channels)

padding tuple[int, int, int, int, int, int]

Padding values in format: (depth_front, depth_back, height_top, height_bottom, width_left, width_right) where: - depth_front/back: padding at start/end of depth axis (z) - height_top/bottom: padding at start/end of height axis (y) - width_left/right: padding at start/end of width axis (x)

value Union[float, collections.abc.Sequence[float]]

Value to fill the padding

Returns:

Type Description ndarray

Padded volume with same number of dimensions as input

Note

The padding order matches the volume dimensions (depth, height, width). For each dimension, the first value is padding at the start (smaller indices), and the second value is padding at the end (larger indices).

Source code in albumentations/augmentations/transforms3d/functional.py Python
def pad_3d_with_params(\n    volume: np.ndarray,\n    padding: tuple[int, int, int, int, int, int],\n    value: ColorType,\n) -> np.ndarray:\n    \"\"\"Pad 3D volume with given parameters.\n\n    Args:\n        volume: Input volume with shape (depth, height, width) or (depth, height, width, channels)\n        padding: Padding values in format:\n            (depth_front, depth_back, height_top, height_bottom, width_left, width_right)\n            where:\n            - depth_front/back: padding at start/end of depth axis (z)\n            - height_top/bottom: padding at start/end of height axis (y)\n            - width_left/right: padding at start/end of width axis (x)\n        value: Value to fill the padding\n\n    Returns:\n        Padded volume with same number of dimensions as input\n\n    Note:\n        The padding order matches the volume dimensions (depth, height, width).\n        For each dimension, the first value is padding at the start (smaller indices),\n        and the second value is padding at the end (larger indices).\n    \"\"\"\n    depth_front, depth_back, height_top, height_bottom, width_left, width_right = padding\n\n    # Skip if no padding is needed\n    if all(p == 0 for p in padding):\n        return volume\n\n    # Handle both 3D and 4D arrays\n    pad_width = [\n        (depth_front, depth_back),  # depth (z) padding\n        (height_top, height_bottom),  # height (y) padding\n        (width_left, width_right),  # width (x) padding\n    ]\n\n    # Add channel padding if 4D array\n    if volume.ndim == NUM_VOLUME_DIMENSIONS:\n        pad_width.append((0, 0))  # no padding for channels\n\n    return np.pad(\n        volume,\n        pad_width=pad_width,\n        mode=\"constant\",\n        constant_values=value,\n    )\n
"},{"location":"api_reference/augmentations/transforms3d/functional/#albumentations.augmentations.transforms3d.functional.transform_cube","title":"def transform_cube (cube, index) [view source on GitHub]","text":"

Transform cube by index (0-47)

Parameters:

Name Type Description cube ndarray

Input array with shape (D, H, W) or (D, H, W, C)

index int

Integer from 0 to 47 specifying which transformation to apply

Returns:

Type Description ndarray

Transformed cube with same shape as input

Source code in albumentations/augmentations/transforms3d/functional.py Python
def transform_cube(cube: np.ndarray, index: int) -> np.ndarray:\n    \"\"\"Transform cube by index (0-47)\n\n    Args:\n        cube: Input array with shape (D, H, W) or (D, H, W, C)\n        index: Integer from 0 to 47 specifying which transformation to apply\n    Returns:\n        Transformed cube with same shape as input\n    \"\"\"\n    if not (0 <= index < 48):\n        raise ValueError(\"Index must be between 0 and 47\")\n\n    # First determine if we need reflection (indices 24-47)\n    needs_reflection = index >= 24\n    working_cube = cube[:, :, ::-1].copy() if needs_reflection else cube.copy()\n    rotation_index = index % 24\n\n    # Map rotation_index (0-23) to specific rotations\n    if rotation_index < 4:\n        # First 4: rotate around axis 0\n        return np.rot90(working_cube, rotation_index, axes=(1, 2))\n\n    if rotation_index < 8:\n        # Next 4: flip 180\u00b0 about axis 1, then rotate around axis 0\n        temp = np.rot90(working_cube, 2, axes=(0, 2))\n        return np.rot90(temp, rotation_index - 4, axes=(1, 2))\n\n    if rotation_index < 16:\n        # Next 8: split between 90\u00b0 and 270\u00b0 about axis 1, then rotate around axis 2\n        if rotation_index < 12:\n            temp = np.rot90(working_cube, axes=(0, 2))\n            return np.rot90(temp, rotation_index - 8, axes=(0, 1))\n        temp = np.rot90(working_cube, -1, axes=(0, 2))\n        return np.rot90(temp, rotation_index - 12, axes=(0, 1))\n\n    # Final 8: split between rotations about axis 2, then rotate around axis 1\n    if rotation_index < 20:\n        temp = np.rot90(working_cube, axes=(0, 1))\n        return np.rot90(temp, rotation_index - 16, axes=(0, 2))\n    temp = np.rot90(working_cube, -1, axes=(0, 1))\n    return np.rot90(temp, rotation_index - 20, axes=(0, 2))\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/","title":"3D (Volumetric) transforms (augmentations.transforms3d.transforms)","text":""},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.BaseCropAndPad3D","title":"class BaseCropAndPad3D (pad_if_needed, fill, fill_mask, pad_position, p=1.0, always_apply=None) [view source on GitHub]","text":"

Base class for 3D transforms that need both cropping and padding.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class BaseCropAndPad3D(Transform3D):\n    \"\"\"Base class for 3D transforms that need both cropping and padding.\"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)\n\n    class InitSchema(Transform3D.InitSchema):\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n        pad_position: Literal[\"center\", \"random\"]\n\n    def __init__(\n        self,\n        pad_if_needed: bool,\n        fill: ColorType,\n        fill_mask: ColorType,\n        pad_position: Literal[\"center\", \"random\"],\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.pad_if_needed = pad_if_needed\n        self.fill = fill\n        self.fill_mask = fill_mask\n        self.pad_position = pad_position\n\n    def _random_pad(self, pad: int) -> tuple[int, int]:\n        \"\"\"Helper function to calculate random padding for one dimension.\"\"\"\n        if pad > 0:\n            pad_start = self.py_random.randint(0, pad)\n            pad_end = pad - pad_start\n        else:\n            pad_start = pad_end = 0\n        return pad_start, pad_end\n\n    def _center_pad(self, pad: int) -> tuple[int, int]:\n        \"\"\"Helper function to calculate center padding for one dimension.\"\"\"\n        pad_start = pad // 2\n        pad_end = pad - pad_start\n        return pad_start, pad_end\n\n    def _get_pad_params(\n        self,\n        image_shape: tuple[int, int, int],\n        target_shape: tuple[int, int, int],\n    ) -> dict[str, Any] | None:\n        \"\"\"Calculate padding parameters if needed for 3D volumes.\"\"\"\n        if not self.pad_if_needed:\n            return None\n\n        z, h, w = image_shape\n        target_z, target_h, target_w = target_shape\n\n        # Calculate total padding needed for each dimension\n        z_pad = max(0, target_z - z)\n        h_pad = max(0, target_h - h)\n        w_pad = max(0, target_w - w)\n\n        if z_pad == 0 and h_pad == 0 and w_pad == 0:\n            return None\n\n        # For center padding, split equally\n        if self.pad_position == \"center\":\n            z_front, z_back = self._center_pad(z_pad)\n            h_top, h_bottom = self._center_pad(h_pad)\n            w_left, w_right = self._center_pad(w_pad)\n        # For random padding, randomly distribute the padding\n        else:  # random\n            z_front, z_back = self._random_pad(z_pad)\n            h_top, h_bottom = self._random_pad(h_pad)\n            w_left, w_right = self._random_pad(w_pad)\n\n        return {\n            \"pad_front\": z_front,\n            \"pad_back\": z_back,\n            \"pad_top\": h_top,\n            \"pad_bottom\": h_bottom,\n            \"pad_left\": w_left,\n            \"pad_right\": w_right,\n        }\n\n    def apply_to_volume(\n        self,\n        volume: np.ndarray,\n        crop_coords: tuple[int, int, int, int, int, int],\n        pad_params: dict[str, int] | None,\n        **params: Any,\n    ) -> np.ndarray:\n        # First crop\n        cropped = f3d.crop3d(volume, crop_coords)\n\n        # Then pad if needed\n        if pad_params is not None:\n            padding = (\n                pad_params[\"pad_front\"],\n                pad_params[\"pad_back\"],\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n            )\n            return f3d.pad_3d_with_params(\n                cropped,\n                padding=padding,\n                value=cast(ColorType, self.fill),\n            )\n\n        return cropped\n\n    def apply_to_mask3d(\n        self,\n        mask3d: np.ndarray,\n        crop_coords: tuple[int, int, int, int, int, int],\n        pad_params: dict[str, int] | None,\n        **params: Any,\n    ) -> np.ndarray:\n        # First crop\n        cropped = f3d.crop3d(mask3d, crop_coords)\n\n        # Then pad if needed\n        if pad_params is not None:\n            padding = (\n                pad_params[\"pad_front\"],\n                pad_params[\"pad_back\"],\n                pad_params[\"pad_top\"],\n                pad_params[\"pad_bottom\"],\n                pad_params[\"pad_left\"],\n                pad_params[\"pad_right\"],\n            )\n            return f3d.pad_3d_with_params(\n                cropped,\n                padding=padding,\n                value=cast(ColorType, self.fill_mask),\n            )\n\n        return cropped\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        crop_coords: tuple[int, int, int, int, int, int],\n        pad_params: dict[str, int] | None,\n        **params: Any,\n    ) -> np.ndarray:\n        # Extract crop start coordinates (z1,y1,x1)\n        crop_z1, _, crop_y1, _, crop_x1, _ = crop_coords\n\n        # Initialize shift vector with negative crop coordinates\n        shift = np.array(\n            [\n                -crop_x1,  # X shift\n                -crop_y1,  # Y shift\n                -crop_z1,  # Z shift\n            ],\n        )\n\n        # Add padding shift if needed\n        if pad_params is not None:\n            shift += np.array(\n                [\n                    pad_params[\"pad_left\"],  # X shift\n                    pad_params[\"pad_top\"],  # Y shift\n                    pad_params[\"pad_front\"],  # Z shift\n                ],\n            )\n\n        # Apply combined shift\n        return fgeometric.shift_keypoints(keypoints, shift)\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.BasePad3D","title":"class BasePad3D (fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Base class for 3D padding transforms.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class BasePad3D(Transform3D):\n    \"\"\"Base class for 3D padding transforms.\"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)\n\n    class InitSchema(Transform3D.InitSchema):\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def apply_to_volume(\n        self,\n        volume: np.ndarray,\n        padding: tuple[int, int, int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        if padding == (0, 0, 0, 0, 0, 0):\n            return volume\n        return f3d.pad_3d_with_params(\n            volume=volume,\n            padding=padding,\n            value=cast(ColorType, self.fill),\n        )\n\n    def apply_to_mask3d(\n        self,\n        mask3d: np.ndarray,\n        padding: tuple[int, int, int, int, int, int],\n        **params: Any,\n    ) -> np.ndarray:\n        if padding == (0, 0, 0, 0, 0, 0):\n            return mask3d\n        return f3d.pad_3d_with_params(\n            volume=mask3d,\n            padding=padding,\n            value=cast(ColorType, self.fill_mask),\n        )\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        padding = params[\"padding\"]\n        shift_vector = np.array([padding[4], padding[2], padding[0]])\n        return fgeometric.shift_keypoints(keypoints, shift_vector)\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CenterCrop3D","title":"class CenterCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop the center of 3D volume.

Parameters:

Name Type Description size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

If you want to perform cropping only in the XY plane while preserving all slices along the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CenterCrop3D(BaseCropAndPad3D):\n    \"\"\"Crop the center of 3D volume.\n\n    Args:\n        size (tuple[int, int, int]): Desired output size of the crop in format (depth, height, width)\n        pad_if_needed (bool): Whether to pad if the volume is smaller than desired crop size. Default: False\n        fill (ColorType): Padding value for image if pad_if_needed is True. Default: 0\n        fill_mask (ColorType): Padding value for mask if pad_if_needed is True. Default: 0\n        p (float): probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If you want to perform cropping only in the XY plane while preserving all slices along\n        the Z axis, consider using CenterCrop instead. CenterCrop will apply the same XY crop\n        to each slice independently, maintaining the full depth of the volume.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        size: Annotated[tuple[int, int, int], AfterValidator(check_range_bounds(1, None))]\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        size: tuple[int, int, int],\n        pad_if_needed: bool = False,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=\"center\",  # Center crop always uses center padding\n            p=p,\n        )\n        self.size = size\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        volume = data[\"volume\"]\n        z, h, w = volume.shape[:3]\n        target_z, target_h, target_w = self.size\n\n        # Get padding params if needed\n        pad_params = self._get_pad_params(\n            image_shape=(z, h, w),\n            target_shape=self.size,\n        )\n\n        # Update dimensions if padding is applied\n        if pad_params is not None:\n            z = z + pad_params[\"pad_front\"] + pad_params[\"pad_back\"]\n            h = h + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            w = w + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n        # Validate dimensions after padding\n        if z < target_z or h < target_h or w < target_w:\n            msg = (\n                f\"Crop size {self.size} is larger than padded image size ({z}, {h}, {w}). \"\n                f\"This should not happen - please report this as a bug.\"\n            )\n            raise ValueError(msg)\n\n        # For CenterCrop3D:\n        z_start = (z - target_z) // 2\n        h_start = (h - target_h) // 2\n        w_start = (w - target_w) // 2\n\n        crop_coords = (\n            z_start,\n            z_start + target_z,\n            h_start,\n            h_start + target_h,\n            w_start,\n            w_start + target_w,\n        )\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"pad_if_needed\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CoarseDropout3D","title":"class CoarseDropout3D (num_holes_range=(1, 1), hole_depth_range=(0.1, 0.2), hole_height_range=(0.1, 0.2), hole_width_range=(0.1, 0.2), fill=0, fill_mask=None, p=0.5, always_apply=None) [view source on GitHub]","text":"

CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally, the corresponding regions in an associated 3D mask, to simulate occlusion and varied object sizes found in real-world volumetric data.

Parameters:

Name Type Description num_holes_range tuple[int, int]

Range (min, max) for the number of cuboid regions to drop out. Default: (1, 1)

hole_depth_range tuple[float, float]

Range (min, max) for the depth of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)

hole_height_range tuple[float, float]

Range (min, max) for the height of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)

hole_width_range tuple[float, float]

Range (min, max) for the width of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)

fill ColorType

Value for the dropped voxels. Can be: - int or float: all channels are filled with this value - tuple: tuple of values for each channel Default: 0

fill_mask ColorType | None

Fill value for dropout regions in the 3D mask. If None, mask regions corresponding to volume dropouts are unchanged. Default: None

p float

Probability of applying the transform. Default: 0.5

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

  • The actual number and size of dropout regions are randomly chosen within the specified ranges.
  • All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.
  • If you want to apply dropout only in the XY plane while preserving the full depth dimension, consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout to each slice independently, effectively creating cylindrical dropout regions that extend through the entire depth of the volume.

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n>>> aug = A.CoarseDropout3D(\n...     num_holes_range=(3, 6),\n...     hole_depth_range=(0.1, 0.2),\n...     hole_height_range=(0.1, 0.2),\n...     hole_width_range=(0.1, 0.2),\n...     fill=0,\n...     p=1.0\n... )\n>>> transformed = aug(volume=volume, mask3d=mask3d)\n>>> transformed_volume, transformed_mask3d = transformed[\"volume\"], transformed[\"mask3d\"]\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CoarseDropout3D(Transform3D):\n    \"\"\"CoarseDropout3D randomly drops out cuboid regions from a 3D volume and optionally,\n    the corresponding regions in an associated 3D mask, to simulate occlusion and\n    varied object sizes found in real-world volumetric data.\n\n    Args:\n        num_holes_range (tuple[int, int]): Range (min, max) for the number of cuboid\n            regions to drop out. Default: (1, 1)\n        hole_depth_range (tuple[float, float]): Range (min, max) for the depth\n            of dropout regions as a fraction of the volume depth (between 0 and 1). Default: (0.1, 0.2)\n        hole_height_range (tuple[float, float]): Range (min, max) for the height\n            of dropout regions as a fraction of the volume height (between 0 and 1). Default: (0.1, 0.2)\n        hole_width_range (tuple[float, float]): Range (min, max) for the width\n            of dropout regions as a fraction of the volume width (between 0 and 1). Default: (0.1, 0.2)\n        fill (ColorType): Value for the dropped voxels. Can be:\n            - int or float: all channels are filled with this value\n            - tuple: tuple of values for each channel\n            Default: 0\n        fill_mask (ColorType | None): Fill value for dropout regions in the 3D mask.\n            If None, mask regions corresponding to volume dropouts are unchanged. Default: None\n        p (float): Probability of applying the transform. Default: 0.5\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - The actual number and size of dropout regions are randomly chosen within the specified ranges.\n        - All values in hole_depth_range, hole_height_range and hole_width_range must be between 0 and 1.\n        - If you want to apply dropout only in the XY plane while preserving the full depth dimension,\n          consider using CoarseDropout instead. CoarseDropout will apply the same rectangular dropout\n          to each slice independently, effectively creating cylindrical dropout regions that extend\n          through the entire depth of the volume.\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n        >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n        >>> aug = A.CoarseDropout3D(\n        ...     num_holes_range=(3, 6),\n        ...     hole_depth_range=(0.1, 0.2),\n        ...     hole_height_range=(0.1, 0.2),\n        ...     hole_width_range=(0.1, 0.2),\n        ...     fill=0,\n        ...     p=1.0\n        ... )\n        >>> transformed = aug(volume=volume, mask3d=mask3d)\n        >>> transformed_volume, transformed_mask3d = transformed[\"volume\"], transformed[\"mask3d\"]\n    \"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)\n\n    class InitSchema(Transform3D.InitSchema):\n        num_holes_range: Annotated[\n            tuple[int, int],\n            AfterValidator(check_range_bounds(0, None)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_depth_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_height_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        hole_width_range: Annotated[\n            tuple[float, float],\n            AfterValidator(check_range_bounds(0, 1)),\n            AfterValidator(nondecreasing),\n        ]\n        fill: ColorType\n        fill_mask: ColorType | None\n\n        @staticmethod\n        def validate_range(range_value: tuple[float, float], range_name: str) -> None:\n            if not 0 <= range_value[0] <= range_value[1] <= 1:\n                raise ValueError(\n                    f\"All values in {range_name} should be in [0, 1] range and first value \"\n                    f\"should be less or equal than the second value. Got: {range_value}\",\n                )\n\n        @model_validator(mode=\"after\")\n        def check_ranges(self) -> Self:\n            self.validate_range(self.hole_depth_range, \"hole_depth_range\")\n            self.validate_range(self.hole_height_range, \"hole_height_range\")\n            self.validate_range(self.hole_width_range, \"hole_width_range\")\n            return self\n\n    def __init__(\n        self,\n        num_holes_range: tuple[int, int] = (1, 1),\n        hole_depth_range: tuple[float, float] = (0.1, 0.2),\n        hole_height_range: tuple[float, float] = (0.1, 0.2),\n        hole_width_range: tuple[float, float] = (0.1, 0.2),\n        fill: ColorType = 0,\n        fill_mask: ColorType | None = None,\n        p: float = 0.5,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n        self.num_holes_range = num_holes_range\n        self.hole_depth_range = hole_depth_range\n        self.hole_height_range = hole_height_range\n        self.hole_width_range = hole_width_range\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def calculate_hole_dimensions(\n        self,\n        volume_shape: tuple[int, int, int],\n        depth_range: tuple[float, float],\n        height_range: tuple[float, float],\n        width_range: tuple[float, float],\n        size: int,\n    ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:\n        \"\"\"Calculate random hole dimensions based on the provided ranges.\"\"\"\n        depth, height, width = volume_shape[:3]\n\n        hole_depths = np.maximum(1, np.ceil(depth * self.random_generator.uniform(*depth_range, size=size))).astype(int)\n        hole_heights = np.maximum(1, np.ceil(height * self.random_generator.uniform(*height_range, size=size))).astype(\n            int,\n        )\n        hole_widths = np.maximum(1, np.ceil(width * self.random_generator.uniform(*width_range, size=size))).astype(int)\n\n        return hole_depths, hole_heights, hole_widths\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        volume_shape = data[\"volume\"].shape[:3]\n\n        num_holes = self.py_random.randint(*self.num_holes_range)\n\n        hole_depths, hole_heights, hole_widths = self.calculate_hole_dimensions(\n            volume_shape,\n            self.hole_depth_range,\n            self.hole_height_range,\n            self.hole_width_range,\n            size=num_holes,\n        )\n\n        depth, height, width = volume_shape[:3]\n\n        z_min = self.random_generator.integers(0, depth - hole_depths + 1, size=num_holes)\n        y_min = self.random_generator.integers(0, height - hole_heights + 1, size=num_holes)\n        x_min = self.random_generator.integers(0, width - hole_widths + 1, size=num_holes)\n        z_max = z_min + hole_depths\n        y_max = y_min + hole_heights\n        x_max = x_min + hole_widths\n\n        holes = np.stack([z_min, y_min, x_min, z_max, y_max, x_max], axis=-1)\n\n        return {\"holes\": holes}\n\n    def apply_to_volume(self, volume: np.ndarray, holes: np.ndarray, **params: Any) -> np.ndarray:\n        if holes.size == 0:\n            return volume\n\n        return f3d.cutout3d(volume, holes, cast(ColorType, self.fill))\n\n    def apply_to_mask(self, mask: np.ndarray, holes: np.ndarray, **params: Any) -> np.ndarray:\n        if self.fill_mask is None or holes.size == 0:\n            return mask\n\n        return f3d.cutout3d(mask, holes, cast(ColorType, self.fill_mask))\n\n    def apply_to_keypoints(\n        self,\n        keypoints: np.ndarray,\n        holes: np.ndarray,\n        **params: Any,\n    ) -> np.ndarray:\n        \"\"\"Remove keypoints that fall within dropout regions.\"\"\"\n        if holes.size == 0:\n            return keypoints\n        processor = cast(KeypointsProcessor, self.get_processor(\"keypoints\"))\n\n        if processor is None or not processor.params.remove_invisible:\n            return keypoints\n        return f3d.filter_keypoints_in_holes3d(keypoints, holes)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"num_holes_range\",\n            \"hole_depth_range\",\n            \"hole_height_range\",\n            \"hole_width_range\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.CubicSymmetry","title":"class CubicSymmetry (p=1.0, always_apply=None) [view source on GitHub]","text":"

Applies a random cubic symmetry transformation to a 3D volume.

This transform is a 3D extension of D4. While D4 handles the 8 symmetries of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube. Like D4, this transform does not create any interpolation artifacts as it only remaps voxels from one position to another without any interpolation.

The 48 transformations consist of: - 24 rotations (orientation-preserving): * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24) - 24 rotoreflections (orientation-reversing): * Reflection through a plane followed by any of the 24 rotations

For a cube, these transformations preserve: - All face centers (6) - All vertex positions (8) - All edge centers (12)

works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

  • This transform is particularly useful for data augmentation in 3D medical imaging, crystallography, and voxel-based 3D modeling where the object's orientation is arbitrary.
  • All transformations preserve the object's chirality (handedness) when using pure rotations (indices 0-23) and invert it when using rotoreflections (indices 24-47).

Examples:

Python
>>> import numpy as np\n>>> import albumentations as A\n>>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n>>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n>>> transform = A.CubicSymmetry(p=1.0)\n>>> transformed = transform(volume=volume, mask3d=mask3d)\n>>> transformed_volume = transformed[\"volume\"]\n>>> transformed_mask3d = transformed[\"mask3d\"]\n

See Also: - D4: The 2D version that handles the 8 symmetries of a square

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class CubicSymmetry(Transform3D):\n    \"\"\"Applies a random cubic symmetry transformation to a 3D volume.\n\n    This transform is a 3D extension of D4. While D4 handles the 8 symmetries\n    of a square (4 rotations x 2 reflections), CubicSymmetry handles all 48 symmetries of a cube.\n    Like D4, this transform does not create any interpolation artifacts as it only remaps voxels\n    from one position to another without any interpolation.\n\n    The 48 transformations consist of:\n    - 24 rotations (orientation-preserving):\n        * 4 rotations around each face diagonal (6 face diagonals x 4 rotations = 24)\n    - 24 rotoreflections (orientation-reversing):\n        * Reflection through a plane followed by any of the 24 rotations\n\n    For a cube, these transformations preserve:\n    - All face centers (6)\n    - All vertex positions (8)\n    - All edge centers (12)\n\n    works with 3D volumes and masks of the shape (D, H, W) or (D, H, W, C)\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        - This transform is particularly useful for data augmentation in 3D medical imaging,\n          crystallography, and voxel-based 3D modeling where the object's orientation\n          is arbitrary.\n        - All transformations preserve the object's chirality (handedness) when using\n          pure rotations (indices 0-23) and invert it when using rotoreflections\n          (indices 24-47).\n\n    Example:\n        >>> import numpy as np\n        >>> import albumentations as A\n        >>> volume = np.random.randint(0, 256, (10, 100, 100), dtype=np.uint8)  # (D, H, W)\n        >>> mask3d = np.random.randint(0, 2, (10, 100, 100), dtype=np.uint8)    # (D, H, W)\n        >>> transform = A.CubicSymmetry(p=1.0)\n        >>> transformed = transform(volume=volume, mask3d=mask3d)\n        >>> transformed_volume = transformed[\"volume\"]\n        >>> transformed_mask3d = transformed[\"mask3d\"]\n\n    See Also:\n        - D4: The 2D version that handles the 8 symmetries of a square\n    \"\"\"\n\n    _targets = (Targets.VOLUME, Targets.MASK3D, Targets.KEYPOINTS)\n\n    def __init__(\n        self,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(p=p, always_apply=always_apply)\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        # Randomly select one of 48 possible transformations\n\n        volume_shape = data[\"volume\"].shape\n        return {\"index\": self.py_random.randint(0, 47), \"volume_shape\": volume_shape}\n\n    def apply_to_volume(self, volume: np.ndarray, index: int, **params: Any) -> np.ndarray:\n        return f3d.transform_cube(volume, index)\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, index: int, **params: Any) -> np.ndarray:\n        return f3d.transform_cube_keypoints(keypoints, index, volume_shape=params[\"volume_shape\"])\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.Pad3D","title":"class Pad3D (padding, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pad the sides of a 3D volume by specified number of voxels.

Parameters:

Name Type Description padding int, tuple[int, int, int] or tuple[int, int, int, int, int, int]

Padding values. Can be: * int - pad all sides by this value * tuple[int, int, int] - symmetric padding (depth, height, width) where each value is applied to both sides of the corresponding dimension * tuple[int, int, int, int, int, int] - explicit padding per side in order: (depth_front, depth_back, height_top, height_bottom, width_left, width_right)

fill ColorType

Padding value for image

fill_mask ColorType

Padding value for mask

p float

probability of applying the transform. Default: 1.0.

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class Pad3D(BasePad3D):\n    \"\"\"Pad the sides of a 3D volume by specified number of voxels.\n\n    Args:\n        padding (int, tuple[int, int, int] or tuple[int, int, int, int, int, int]): Padding values. Can be:\n            * int - pad all sides by this value\n            * tuple[int, int, int] - symmetric padding (depth, height, width) where each value\n              is applied to both sides of the corresponding dimension\n            * tuple[int, int, int, int, int, int] - explicit padding per side in order:\n              (depth_front, depth_back, height_top, height_bottom, width_left, width_right)\n\n        fill (ColorType): Padding value for image\n        fill_mask (ColorType): Padding value for mask\n        p (float): probability of applying the transform. Default: 1.0.\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),\n        with optional channel dimension as the last axis.\n    \"\"\"\n\n    class InitSchema(BasePad3D.InitSchema):\n        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int]\n\n        @field_validator(\"padding\")\n        @classmethod\n        def validate_padding(\n            cls,\n            v: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],\n        ) -> int | tuple[int, int, int] | tuple[int, int, int, int, int, int]:\n            if isinstance(v, int) and v < 0:\n                raise ValueError(\"Padding value must be non-negative\")\n            if isinstance(v, tuple) and not all(isinstance(i, int) and i >= 0 for i in v):\n                raise ValueError(\"Padding tuple must contain non-negative integers\")\n\n            return v\n\n    def __init__(\n        self,\n        padding: int | tuple[int, int, int] | tuple[int, int, int, int, int, int],\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.padding = padding\n        self.fill = fill\n        self.fill_mask = fill_mask\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        if isinstance(self.padding, int):\n            pad_d = pad_h = pad_w = self.padding\n            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)\n        elif len(self.padding) == NUM_DIMENSIONS:\n            pad_d, pad_h, pad_w = self.padding  # type: ignore[misc]\n            padding = (pad_d, pad_d, pad_h, pad_h, pad_w, pad_w)\n        else:\n            padding = self.padding  # type: ignore[assignment]\n\n        return {\"padding\": padding}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"padding\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.PadIfNeeded3D","title":"class PadIfNeeded3D (min_zyx=None, pad_divisor_zyx=None, position='center', fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions. If the pad_divisor_zyx is specified, the function additionally ensures that the volume dimensions are divisible by these values.

Parameters:

Name Type Description min_zyx tuple[int, int, int] | None

Minimum desired size as (depth, height, width). Ensures volume dimensions are at least these values. If not specified, pad_divisor_zyx must be provided.

pad_divisor_zyx tuple[int, int, int] | None

If set, pads each dimension to make it divisible by corresponding value in format (depth_div, height_div, width_div). If not specified, min_zyx must be provided.

position Literal[\"center\", \"random\"]

Position where the volume is to be placed after padding. Default is 'center'.

fill ColorType

Value to fill the border voxels for volume. Default: 0

fill_mask ColorType

Value to fill the border voxels for masks. Default: 0

p float

Probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width), with optional channel dimension as the last axis.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class PadIfNeeded3D(BasePad3D):\n    \"\"\"Pads the sides of a 3D volume if its dimensions are less than specified minimum dimensions.\n    If the pad_divisor_zyx is specified, the function additionally ensures that the volume\n    dimensions are divisible by these values.\n\n    Args:\n        min_zyx (tuple[int, int, int] | None): Minimum desired size as (depth, height, width).\n            Ensures volume dimensions are at least these values.\n            If not specified, pad_divisor_zyx must be provided.\n        pad_divisor_zyx (tuple[int, int, int] | None): If set, pads each dimension to make it\n            divisible by corresponding value in format (depth_div, height_div, width_div).\n            If not specified, min_zyx must be provided.\n        position (Literal[\"center\", \"random\"]): Position where the volume is to be placed after padding.\n            Default is 'center'.\n        fill (ColorType): Value to fill the border voxels for volume. Default: 0\n        fill_mask (ColorType): Value to fill the border voxels for masks. Default: 0\n        p (float): Probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        Input volume should be a numpy array with dimensions ordered as (z, y, x) or (depth, height, width),\n        with optional channel dimension as the last axis.\n    \"\"\"\n\n    class InitSchema(BasePad3D.InitSchema):\n        min_zyx: Annotated[tuple[int, int, int] | None, AfterValidator(check_range_bounds(0, None))]\n        pad_divisor_zyx: Annotated[tuple[int, int, int] | None, AfterValidator(check_range_bounds(1, None))]\n        position: Literal[\"center\", \"random\"]\n\n        @model_validator(mode=\"after\")\n        def validate_params(self) -> Self:\n            if self.min_zyx is None and self.pad_divisor_zyx is None:\n                msg = \"At least one of min_zyx or pad_divisor_zyx must be set\"\n                raise ValueError(msg)\n            return self\n\n    def __init__(\n        self,\n        min_zyx: tuple[int, int, int] | None = None,\n        pad_divisor_zyx: tuple[int, int, int] | None = None,\n        position: Literal[\"center\", \"random\"] = \"center\",\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(fill=fill, fill_mask=fill_mask, p=p)\n        self.min_zyx = min_zyx\n        self.pad_divisor_zyx = pad_divisor_zyx\n        self.position = position\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        depth, height, width = data[\"volume\"].shape[:3]\n        sizes = (depth, height, width)\n\n        paddings = [\n            fgeometric.get_dimension_padding(\n                current_size=size,\n                min_size=self.min_zyx[i] if self.min_zyx else None,\n                divisor=self.pad_divisor_zyx[i] if self.pad_divisor_zyx else None,\n            )\n            for i, size in enumerate(sizes)\n        ]\n\n        padding = f3d.adjust_padding_by_position3d(\n            paddings=paddings,\n            position=self.position,\n            py_random=self.py_random,\n        )\n\n        return {\"padding\": padding}\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\n            \"min_zyx\",\n            \"pad_divisor_zyx\",\n            \"position\",\n            \"fill\",\n            \"fill_mask\",\n        )\n
"},{"location":"api_reference/augmentations/transforms3d/transforms/#albumentations.augmentations.transforms3d.transforms.RandomCrop3D","title":"class RandomCrop3D (size, pad_if_needed=False, fill=0, fill_mask=0, p=1.0, always_apply=None) [view source on GitHub]","text":"

Crop random part of 3D volume.

Parameters:

Name Type Description size tuple[int, int, int]

Desired output size of the crop in format (depth, height, width)

pad_if_needed bool

Whether to pad if the volume is smaller than desired crop size. Default: False

fill ColorType

Padding value for image if pad_if_needed is True. Default: 0

fill_mask ColorType

Padding value for mask if pad_if_needed is True. Default: 0

p float

probability of applying the transform. Default: 1.0

Targets

volume, mask3d, keypoints

Image types: uint8, float32

Note

If you want to perform random cropping only in the XY plane while preserving all slices along the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop to each slice independently, maintaining the full depth of the volume.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/augmentations/transforms3d/transforms.py Python
class RandomCrop3D(BaseCropAndPad3D):\n    \"\"\"Crop random part of 3D volume.\n\n    Args:\n        size (tuple[int, int, int]): Desired output size of the crop in format (depth, height, width)\n        pad_if_needed (bool): Whether to pad if the volume is smaller than desired crop size. Default: False\n        fill (ColorType): Padding value for image if pad_if_needed is True. Default: 0\n        fill_mask (ColorType): Padding value for mask if pad_if_needed is True. Default: 0\n        p (float): probability of applying the transform. Default: 1.0\n\n    Targets:\n        volume, mask3d, keypoints\n\n    Image types:\n        uint8, float32\n\n    Note:\n        If you want to perform random cropping only in the XY plane while preserving all slices along\n        the Z axis, consider using RandomCrop instead. RandomCrop will apply the same XY crop\n        to each slice independently, maintaining the full depth of the volume.\n    \"\"\"\n\n    class InitSchema(BaseTransformInitSchema):\n        size: Annotated[tuple[int, int, int], AfterValidator(check_range_bounds(1, None))]\n        pad_if_needed: bool\n        fill: ColorType\n        fill_mask: ColorType\n\n    def __init__(\n        self,\n        size: tuple[int, int, int],\n        pad_if_needed: bool = False,\n        fill: ColorType = 0,\n        fill_mask: ColorType = 0,\n        p: float = 1.0,\n        always_apply: bool | None = None,\n    ):\n        super().__init__(\n            pad_if_needed=pad_if_needed,\n            fill=fill,\n            fill_mask=fill_mask,\n            pad_position=\"random\",  # Random crop uses random padding position\n            p=p,\n        )\n        self.size = size\n\n    def get_params_dependent_on_data(\n        self,\n        params: dict[str, Any],\n        data: dict[str, Any],\n    ) -> dict[str, Any]:\n        volume = data[\"volume\"]\n        z, h, w = volume.shape[:3]\n        target_z, target_h, target_w = self.size\n\n        # Get padding params if needed\n        pad_params = self._get_pad_params(\n            image_shape=(z, h, w),\n            target_shape=self.size,\n        )\n\n        # Update dimensions if padding is applied\n        if pad_params is not None:\n            z = z + pad_params[\"pad_front\"] + pad_params[\"pad_back\"]\n            h = h + pad_params[\"pad_top\"] + pad_params[\"pad_bottom\"]\n            w = w + pad_params[\"pad_left\"] + pad_params[\"pad_right\"]\n\n        # Calculate random crop coordinates\n        z_start = self.py_random.randint(0, max(0, z - target_z))\n        h_start = self.py_random.randint(0, max(0, h - target_h))\n        w_start = self.py_random.randint(0, max(0, w - target_w))\n\n        crop_coords = (\n            z_start,\n            z_start + target_z,\n            h_start,\n            h_start + target_h,\n            w_start,\n            w_start + target_w,\n        )\n\n        return {\n            \"crop_coords\": crop_coords,\n            \"pad_params\": pad_params,\n        }\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return \"size\", \"pad_if_needed\", \"fill\", \"fill_mask\"\n
"},{"location":"api_reference/core/","title":"Index","text":"
  • Composition API (albumentations.core.composition)
  • Serialization API (albumentations.core.serialization)
  • Transforms Interface (albumentations.core.transforms_interface)
  • Helper functions for working with bounding boxes (albumentations.core.bbox_utils)
  • Helper functions for working with keypoints (albumentations.core.keypoints_utils)
"},{"location":"api_reference/core/bbox_utils/","title":"Helper functions for working with bounding boxes (augmentations.core.bbox_utils)","text":""},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.BboxParams","title":"class BboxParams (format, label_fields=None, min_area=0.0, min_visibility=0.0, min_width=0.0, min_height=0.0, check_each_transform=True, clip=False) [view source on GitHub]","text":"

Parameters of bounding boxes

Parameters:

Name Type Description format Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"]

format of bounding boxes.

The coco format [x_min, y_min, width, height], e.g. [97, 12, 150, 200]. The pascal_voc format [x_min, y_min, x_max, y_max], e.g. [97, 12, 247, 212]. The albumentations format is like pascal_voc, but normalized, in other words: [x_min, y_min, x_max, y_max], e.g. [0.2, 0.3, 0.4, 0.5]. The yolo format [x, y, width, height], e.g. [0.1, 0.2, 0.3, 0.4]; x, y - normalized bbox center; width, height - normalized bbox width and height.

label_fields list

List of fields joined with boxes, e.g., labels.

min_area float

Minimum area of a bounding box in pixels or normalized units. Bounding boxes with an area less than this value will be removed. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain in the list. Bounding boxes with a visible area less than this fraction will be removed. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels or normalized units. Bounding boxes with a width less than this value will be removed. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels or normalized units. Bounding boxes with a height less than this value will be removed. Default: 0.0.

check_each_transform bool

If True, bounding boxes will be checked after each dual transform. Default: True.

clip bool

If True, bounding boxes will be clipped to the image borders before applying any transform. Default: False.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/bbox_utils.py Python
class BboxParams(Params):\n    \"\"\"Parameters of bounding boxes\n\n    Args:\n        format Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"]: format of bounding boxes.\n\n            The `coco` format\n                `[x_min, y_min, width, height]`, e.g. [97, 12, 150, 200].\n            The `pascal_voc` format\n                `[x_min, y_min, x_max, y_max]`, e.g. [97, 12, 247, 212].\n            The `albumentations` format\n                is like `pascal_voc`, but normalized,\n                in other words: `[x_min, y_min, x_max, y_max]`, e.g. [0.2, 0.3, 0.4, 0.5].\n            The `yolo` format\n                `[x, y, width, height]`, e.g. [0.1, 0.2, 0.3, 0.4];\n                `x`, `y` - normalized bbox center; `width`, `height` - normalized bbox width and height.\n\n        label_fields (list): List of fields joined with boxes, e.g., labels.\n        min_area (float): Minimum area of a bounding box in pixels or normalized units.\n            Bounding boxes with an area less than this value will be removed. Default: 0.0.\n        min_visibility (float): Minimum fraction of area for a bounding box to remain in the list.\n            Bounding boxes with a visible area less than this fraction will be removed. Default: 0.0.\n        min_width (float): Minimum width of a bounding box in pixels or normalized units.\n            Bounding boxes with a width less than this value will be removed. Default: 0.0.\n        min_height (float): Minimum height of a bounding box in pixels or normalized units.\n            Bounding boxes with a height less than this value will be removed. Default: 0.0.\n        check_each_transform (bool): If True, bounding boxes will be checked after each dual transform. Default: True.\n        clip (bool): If True, bounding boxes will be clipped to the image borders before applying any transform.\n            Default: False.\n\n    \"\"\"\n\n    def __init__(\n        self,\n        format: Literal[\"coco\", \"pascal_voc\", \"albumentations\", \"yolo\"],  # noqa: A002\n        label_fields: Sequence[Any] | None = None,\n        min_area: float = 0.0,\n        min_visibility: float = 0.0,\n        min_width: float = 0.0,\n        min_height: float = 0.0,\n        check_each_transform: bool = True,\n        clip: bool = False,\n    ):\n        super().__init__(format, label_fields)\n        self.min_area = min_area\n        self.min_visibility = min_visibility\n        self.min_width = min_width\n        self.min_height = min_height\n        self.check_each_transform = check_each_transform\n        self.clip = clip\n\n    def to_dict_private(self) -> dict[str, Any]:\n        data = super().to_dict_private()\n        data.update(\n            {\n                \"min_area\": self.min_area,\n                \"min_visibility\": self.min_visibility,\n                \"min_width\": self.min_width,\n                \"min_height\": self.min_height,\n                \"check_each_transform\": self.check_each_transform,\n                \"clip\": self.clip,\n            },\n        )\n        return data\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return \"BboxParams\"\n\n    def __repr__(self) -> str:\n        return (\n            f\"BboxParams(format={self.format}, label_fields={self.label_fields}, min_area={self.min_area},\"\n            f\" min_visibility={self.min_visibility}, min_width={self.min_width}, min_height={self.min_height},\"\n            f\" check_each_transform={self.check_each_transform}, clip={self.clip})\"\n        )\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.bboxes_from_masks","title":"def bboxes_from_masks (masks) [view source on GitHub]","text":"

Create bounding boxes from binary masks (fast version)

Parameters:

Name Type Description masks np.ndarray

Binary masks of shape (H, W) or (N, H, W) where N is the number of masks, and H, W are the height and width of each mask.

Returns:

Type Description np.ndarray

An array of bounding boxes with shape (N, 4), where each row is (x_min, y_min, x_max, y_max).

Source code in albumentations/core/bbox_utils.py Python
def bboxes_from_masks(masks: np.ndarray) -> np.ndarray:\n    \"\"\"Create bounding boxes from binary masks (fast version)\n\n    Args:\n        masks (np.ndarray): Binary masks of shape (H, W) or (N, H, W) where N is the number of masks,\n                           and H, W are the height and width of each mask.\n\n    Returns:\n        np.ndarray: An array of bounding boxes with shape (N, 4), where each row is\n                   (x_min, y_min, x_max, y_max).\n    \"\"\"\n    # Handle single mask case by adding batch dimension\n    if len(masks.shape) == MONO_CHANNEL_DIMENSIONS:\n        masks = masks[np.newaxis, ...]\n\n    rows = np.any(masks, axis=2)\n    cols = np.any(masks, axis=1)\n\n    bboxes = np.zeros((masks.shape[0], 4), dtype=np.int32)\n\n    for i, (row, col) in enumerate(zip(rows, cols)):\n        if not np.any(row) or not np.any(col):\n            bboxes[i] = [-1, -1, -1, -1]\n        else:\n            y_min, y_max = np.where(row)[0][[0, -1]]\n            x_min, x_max = np.where(col)[0][[0, -1]]\n            bboxes[i] = [x_min, y_min, x_max + 1, y_max + 1]\n\n    return bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.calculate_bbox_areas_in_pixels","title":"def calculate_bbox_areas_in_pixels (bboxes, shape) [view source on GitHub]","text":"

Calculate areas for multiple bounding boxes.

This function computes the areas of bounding boxes given their normalized coordinates and the dimensions of the image they belong to. The bounding boxes are expected to be in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of shape (N, 4+) where N is the number of bounding boxes. Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates. Additional columns beyond the first 4 are ignored.

shape ShapeType

A tuple containing the height and width of the image (height, width).

Returns:

Type Description np.ndarray

A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels. Returns an empty array if the input bboxes is empty.

Note

  • The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min). Invalid bounding boxes may result in negative areas.
  • The function preserves the input array and creates a copy for internal calculations.
  • The returned areas are in pixel units, not normalized.

Examples:

Python
>>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])\n>>> image_shape = (100, 100)\n>>> areas = calculate_bbox_areas(bboxes, image_shape)\n>>> print(areas)\n[1600. 3600.]\n
Source code in albumentations/core/bbox_utils.py Python
def calculate_bbox_areas_in_pixels(bboxes: np.ndarray, shape: ShapeType) -> np.ndarray:\n    \"\"\"Calculate areas for multiple bounding boxes.\n\n    This function computes the areas of bounding boxes given their normalized coordinates\n    and the dimensions of the image they belong to. The bounding boxes are expected to be\n    in the format [x_min, y_min, x_max, y_max] with normalized coordinates (0 to 1).\n\n    Args:\n        bboxes (np.ndarray): A numpy array of shape (N, 4+) where N is the number of bounding boxes.\n                             Each row contains [x_min, y_min, x_max, y_max] in normalized coordinates.\n                             Additional columns beyond the first 4 are ignored.\n        shape (ShapeType): A tuple containing the height and width of the image (height, width).\n\n    Returns:\n        np.ndarray: A 1D numpy array of shape (N,) containing the areas of the bounding boxes in pixels.\n                    Returns an empty array if the input `bboxes` is empty.\n\n    Note:\n        - The function assumes that the input bounding boxes are valid (i.e., x_max > x_min and y_max > y_min).\n          Invalid bounding boxes may result in negative areas.\n        - The function preserves the input array and creates a copy for internal calculations.\n        - The returned areas are in pixel units, not normalized.\n\n    Example:\n        >>> bboxes = np.array([[0.1, 0.1, 0.5, 0.5], [0.2, 0.2, 0.8, 0.8]])\n        >>> image_shape = (100, 100)\n        >>> areas = calculate_bbox_areas(bboxes, image_shape)\n        >>> print(areas)\n        [1600. 3600.]\n    \"\"\"\n    if len(bboxes) == 0:\n        return np.array([], dtype=np.float32)\n\n    height, width = shape[\"height\"], shape[\"width\"]\n    bboxes_denorm = bboxes.copy()\n    bboxes_denorm[:, [0, 2]] *= width\n    bboxes_denorm[:, [1, 3]] *= height\n    return (bboxes_denorm[:, 2] - bboxes_denorm[:, 0]) * (bboxes_denorm[:, 3] - bboxes_denorm[:, 1])\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.check_bboxes","title":"def check_bboxes (bboxes) [view source on GitHub]","text":"

Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.

Parameters:

Name Type Description bboxes np.ndarray

numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.

Exceptions:

Type Description ValueError

If any bbox is invalid.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef check_bboxes(bboxes: np.ndarray) -> None:\n    \"\"\"Check if bboxes boundaries are in range 0, 1 and minimums are lesser than maximums.\n\n    Args:\n        bboxes: numpy array of shape (num_bboxes, 4+) where first 4 coordinates are x_min, y_min, x_max, y_max.\n\n    Raises:\n        ValueError: If any bbox is invalid.\n    \"\"\"\n    # Check if all values are in range [0, 1]\n    in_range = (bboxes[:, :4] >= 0) & (bboxes[:, :4] <= 1)\n    close_to_zero = np.isclose(bboxes[:, :4], 0)\n    close_to_one = np.isclose(bboxes[:, :4], 1)\n    valid_range = in_range | close_to_zero | close_to_one\n\n    if not np.all(valid_range):\n        invalid_idx = np.where(~np.all(valid_range, axis=1))[0][0]\n        invalid_bbox = bboxes[invalid_idx]\n        invalid_coord = [\"x_min\", \"y_min\", \"x_max\", \"y_max\"][np.where(~valid_range[invalid_idx])[0][0]]\n        invalid_value = invalid_bbox[np.where(~valid_range[invalid_idx])[0][0]]\n        raise ValueError(\n            f\"Expected {invalid_coord} for bbox {invalid_bbox} to be in the range [0.0, 1.0], got {invalid_value}.\",\n        )\n\n    # Check if x_max > x_min and y_max > y_min\n    valid_order = (bboxes[:, 2] > bboxes[:, 0]) & (bboxes[:, 3] > bboxes[:, 1])\n\n    if not np.all(valid_order):\n        invalid_idx = np.where(~valid_order)[0][0]\n        invalid_bbox = bboxes[invalid_idx]\n        if invalid_bbox[2] <= invalid_bbox[0]:\n            raise ValueError(f\"x_max is less than or equal to x_min for bbox {invalid_bbox}.\")\n\n        raise ValueError(f\"y_max is less than or equal to y_min for bbox {invalid_bbox}.\")\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.clip_bboxes","title":"def clip_bboxes (bboxes, shape) [view source on GitHub]","text":"

Clips the bounding box coordinates to ensure they fit within the boundaries of an image.

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (num_boxes, 4+) in normalized format. The first 4 columns are [x_min, y_min, x_max, y_max].

image_shape Tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

The clipped bounding boxes, normalized to the image dimensions.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef clip_bboxes(bboxes: np.ndarray, shape: ShapeType) -> np.ndarray:\n    \"\"\"Clips the bounding box coordinates to ensure they fit within the boundaries of an image.\n\n    Parameters:\n        bboxes (np.ndarray): Array of bounding boxes with shape (num_boxes, 4+) in normalized format.\n                             The first 4 columns are [x_min, y_min, x_max, y_max].\n        image_shape (Tuple[int, int]): Image shape (height, width).\n\n    Returns:\n        np.ndarray: The clipped bounding boxes, normalized to the image dimensions.\n\n    \"\"\"\n    height, width = shape[\"height\"], shape[\"width\"]\n\n    # Denormalize bboxes\n    denorm_bboxes = denormalize_bboxes(bboxes, shape)\n\n    ## Note:\n    # It could be tempting to use cols - 1 and rows - 1 as the upper bounds for the clipping\n\n    # But this would cause the bounding box to be clipped to the image dimensions - 1 which is not what we want.\n    # Bounding box lives not in the middle of pixels but between them.\n\n    # Example: for image with height 100, width 100, the pixel values are in the range [0, 99]\n    # but if we want bounding box to be 1 pixel width and height and lie on the boundary of the image\n    # it will be described as [99, 99, 100, 100] => clip by image_size - 1 will lead to [99, 99, 99, 99]\n    # which is incorrect\n\n    # It could be also tempting to clip `x_min`` to `cols - 1`` and `y_min` to `rows - 1`, but this also leads\n    # to another error. If image fully lies outside of the visible area and min_area is set to 0, then\n    # the bounding box will be clipped to the image size - 1 and will be 1 pixel in size and fully visible,\n    # but it should be completely removed.\n\n    # Clip coordinates\n    denorm_bboxes[:, [0, 2]] = np.clip(denorm_bboxes[:, [0, 2]], 0, width, out=denorm_bboxes[:, [0, 2]])\n    denorm_bboxes[:, [1, 3]] = np.clip(denorm_bboxes[:, [1, 3]], 0, height, out=denorm_bboxes[:, [1, 3]])\n\n    # Normalize clipped bboxes\n    return normalize_bboxes(denorm_bboxes, shape)\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.convert_bboxes_from_albumentations","title":"def convert_bboxes_from_albumentations (bboxes, target_format, shape, check_validity=False) [view source on GitHub]","text":"

Convert bounding boxes from the format used by albumentations to a specified format.

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

target_format Literal['coco', 'pascal_voc', 'yolo']

Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.

shape ShapeType

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description np.ndarray

An array of bounding boxes in the target format with shape (num_bboxes, 4+).

Exceptions:

Type Description ValueError

If target_format is not 'coco', 'pascal_voc' or 'yolo'.

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef convert_bboxes_from_albumentations(\n    bboxes: np.ndarray,\n    target_format: Literal[\"coco\", \"pascal_voc\", \"yolo\"],\n    shape: ShapeType,\n    check_validity: bool = False,\n) -> np.ndarray:\n    \"\"\"Convert bounding boxes from the format used by albumentations to a specified format.\n\n    Args:\n        bboxes: A numpy array of albumentations bounding boxes with shape (num_bboxes, 4+).\n                The first 4 columns are [x_min, y_min, x_max, y_max].\n        target_format: Required format of the output bounding boxes. Should be 'coco', 'pascal_voc' or 'yolo'.\n        shape: Image shape (height, width).\n        check_validity: Check if all boxes are valid boxes.\n\n    Returns:\n        np.ndarray: An array of bounding boxes in the target format with shape (num_bboxes, 4+).\n\n    Raises:\n        ValueError: If `target_format` is not 'coco', 'pascal_voc' or 'yolo'.\n    \"\"\"\n    if target_format not in {\"coco\", \"pascal_voc\", \"yolo\"}:\n        raise ValueError(\n            f\"Unknown target_format {target_format}. Supported formats are: 'coco', 'pascal_voc' and 'yolo'\",\n        )\n\n    if check_validity:\n        check_bboxes(bboxes)\n\n    converted_bboxes = np.zeros_like(bboxes)\n    converted_bboxes[:, 4:] = bboxes[:, 4:]  # Preserve additional columns\n\n    denormalized_bboxes = denormalize_bboxes(bboxes[:, :4], shape) if target_format != \"yolo\" else bboxes[:, :4]\n\n    if target_format == \"coco\":\n        converted_bboxes[:, 0] = denormalized_bboxes[:, 0]  # x_min\n        converted_bboxes[:, 1] = denormalized_bboxes[:, 1]  # y_min\n        converted_bboxes[:, 2] = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]  # width\n        converted_bboxes[:, 3] = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]  # height\n    elif target_format == \"yolo\":\n        converted_bboxes[:, 0] = (denormalized_bboxes[:, 0] + denormalized_bboxes[:, 2]) / 2  # x_center\n        converted_bboxes[:, 1] = (denormalized_bboxes[:, 1] + denormalized_bboxes[:, 3]) / 2  # y_center\n        converted_bboxes[:, 2] = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]  # width\n        converted_bboxes[:, 3] = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]  # height\n    else:  # pascal_voc\n        converted_bboxes[:, :4] = denormalized_bboxes\n\n    return converted_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.convert_bboxes_to_albumentations","title":"def convert_bboxes_to_albumentations (bboxes, source_format, shape, check_validity=False) [view source on GitHub]","text":"

Convert bounding boxes from a specified format to the format used by albumentations: normalized coordinates of top-left and bottom-right corners of the bounding box in the form of (x_min, y_min, x_max, y_max) e.g. (0.15, 0.27, 0.67, 0.5).

Parameters:

Name Type Description bboxes np.ndarray

A numpy array of bounding boxes with shape (num_bboxes, 4+).

source_format Literal['coco', 'pascal_voc', 'yolo']

Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.

shape ShapeType

Image shape (height, width).

check_validity bool

Check if all boxes are valid boxes.

Returns:

Type Description np.ndarray

An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).

Exceptions:

Type Description ValueError

If source_format is not 'coco', 'pascal_voc', or 'yolo'.

ValueError

If in YOLO format, any coordinates are not in the range (0, 1].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef convert_bboxes_to_albumentations(\n    bboxes: np.ndarray,\n    source_format: Literal[\"coco\", \"pascal_voc\", \"yolo\"],\n    shape: ShapeType,\n    check_validity: bool = False,\n) -> np.ndarray:\n    \"\"\"Convert bounding boxes from a specified format to the format used by albumentations:\n    normalized coordinates of top-left and bottom-right corners of the bounding box in the form of\n    `(x_min, y_min, x_max, y_max)` e.g. `(0.15, 0.27, 0.67, 0.5)`.\n\n    Args:\n        bboxes: A numpy array of bounding boxes with shape (num_bboxes, 4+).\n        source_format: Format of the input bounding boxes. Should be 'coco', 'pascal_voc', or 'yolo'.\n        shape: Image shape (height, width).\n        check_validity: Check if all boxes are valid boxes.\n\n    Returns:\n        np.ndarray: An array of bounding boxes in albumentations format with shape (num_bboxes, 4+).\n\n    Raises:\n        ValueError: If `source_format` is not 'coco', 'pascal_voc', or 'yolo'.\n        ValueError: If in YOLO format, any coordinates are not in the range (0, 1].\n    \"\"\"\n    if source_format not in {\"coco\", \"pascal_voc\", \"yolo\"}:\n        raise ValueError(\n            f\"Unknown source_format {source_format}. Supported formats are: 'coco', 'pascal_voc' and 'yolo'\",\n        )\n\n    bboxes = bboxes.copy().astype(np.float32)\n    converted_bboxes = np.zeros_like(bboxes)\n    converted_bboxes[:, 4:] = bboxes[:, 4:]  # Preserve additional columns\n\n    if source_format == \"coco\":\n        converted_bboxes[:, 0] = bboxes[:, 0]  # x_min\n        converted_bboxes[:, 1] = bboxes[:, 1]  # y_min\n        converted_bboxes[:, 2] = bboxes[:, 0] + bboxes[:, 2]  # x_max\n        converted_bboxes[:, 3] = bboxes[:, 1] + bboxes[:, 3]  # y_max\n    elif source_format == \"yolo\":\n        if check_validity and np.any((bboxes[:, :4] <= 0) | (bboxes[:, :4] > 1)):\n            raise ValueError(f\"In YOLO format all coordinates must be float and in range (0, 1], got {bboxes}\")\n\n        w_half, h_half = bboxes[:, 2] / 2, bboxes[:, 3] / 2\n        converted_bboxes[:, 0] = bboxes[:, 0] - w_half  # x_min\n        converted_bboxes[:, 1] = bboxes[:, 1] - h_half  # y_min\n        converted_bboxes[:, 2] = bboxes[:, 0] + w_half  # x_max\n        converted_bboxes[:, 3] = bboxes[:, 1] + h_half  # y_max\n    else:  # pascal_voc\n        converted_bboxes[:, :4] = bboxes[:, :4]\n\n    if source_format != \"yolo\":\n        converted_bboxes[:, :4] = normalize_bboxes(converted_bboxes[:, :4], shape)\n\n    if check_validity:\n        check_bboxes(converted_bboxes)\n\n    return converted_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.denormalize_bboxes","title":"def denormalize_bboxes (bboxes, shape) [view source on GitHub]","text":"

Denormalize array of bounding boxes.

Parameters:

Name Type Description bboxes np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

shape ShapeType | tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef denormalize_bboxes(\n    bboxes: np.ndarray,\n    shape: ShapeType | tuple[int, int],\n) -> np.ndarray:\n    \"\"\"Denormalize  array of bounding boxes.\n\n    Args:\n        bboxes: Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n        shape: Image shape `(height, width)`.\n\n    Returns:\n        Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n\n    \"\"\"\n    if isinstance(shape, tuple):\n        rows, cols = shape[:2]\n    else:\n        rows, cols = shape[\"height\"], shape[\"width\"]\n\n    denormalized = bboxes.copy().astype(float)\n    denormalized[:, [0, 2]] *= cols\n    denormalized[:, [1, 3]] *= rows\n    return denormalized\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.filter_bboxes","title":"def filter_bboxes (bboxes, shape, min_area=0.0, min_visibility=0.0, min_width=1.0, min_height=1.0) [view source on GitHub]","text":"

Remove bounding boxes that either lie outside of the visible area by more than min_visibility or whose area in pixels is under the threshold set by min_area. Also crops boxes to final image size.

Parameters:

Name Type Description bboxes np.ndarray

numpy array of bounding boxes with shape (num_bboxes, 4+). The first 4 columns are [x_min, y_min, x_max, y_max].

shape dict[str, int]

The shape of the image/volume: - For 2D: {'height': int, 'width': int} - For 3D: {'height': int, 'width': int, 'depth': int}

min_area float

Minimum area of a bounding box in pixels. Default: 0.0.

min_visibility float

Minimum fraction of area for a bounding box to remain. Default: 0.0.

min_width float

Minimum width of a bounding box in pixels. Default: 0.0.

min_height float

Minimum height of a bounding box in pixels. Default: 0.0.

Returns:

Type Description np.ndarray

numpy array of filtered bounding boxes.

Source code in albumentations/core/bbox_utils.py Python
def filter_bboxes(\n    bboxes: np.ndarray,\n    shape: ShapeType,\n    min_area: float = 0.0,\n    min_visibility: float = 0.0,\n    min_width: float = 1.0,\n    min_height: float = 1.0,\n) -> np.ndarray:\n    \"\"\"Remove bounding boxes that either lie outside of the visible area by more than min_visibility\n    or whose area in pixels is under the threshold set by `min_area`. Also crops boxes to final image size.\n\n    Args:\n        bboxes: numpy array of bounding boxes with shape (num_bboxes, 4+).\n                The first 4 columns are [x_min, y_min, x_max, y_max].\n        shape (dict[str, int]): The shape of the image/volume:\n                               - For 2D: {'height': int, 'width': int}\n                               - For 3D: {'height': int, 'width': int, 'depth': int}\n\n        min_area: Minimum area of a bounding box in pixels. Default: 0.0.\n        min_visibility: Minimum fraction of area for a bounding box to remain. Default: 0.0.\n        min_width: Minimum width of a bounding box in pixels. Default: 0.0.\n        min_height: Minimum height of a bounding box in pixels. Default: 0.0.\n\n    Returns:\n        numpy array of filtered bounding boxes.\n    \"\"\"\n    epsilon = 1e-7\n\n    if len(bboxes) == 0:\n        return np.array([], dtype=np.float32).reshape(0, 4)\n\n    # Calculate areas of bounding boxes before clipping in pixels\n    denormalized_box_areas = calculate_bbox_areas_in_pixels(bboxes, shape)\n\n    # Clip bounding boxes in ratio\n    clipped_bboxes = clip_bboxes(bboxes, shape)\n\n    # Calculate areas of clipped bounding boxes in pixels\n    clipped_box_areas = calculate_bbox_areas_in_pixels(clipped_bboxes, shape)\n\n    # Calculate width and height of the clipped bounding boxes\n    denormalized_bboxes = denormalize_bboxes(clipped_bboxes[:, :4], shape)\n\n    clipped_widths = denormalized_bboxes[:, 2] - denormalized_bboxes[:, 0]\n    clipped_heights = denormalized_bboxes[:, 3] - denormalized_bboxes[:, 1]\n\n    # Create a mask for bboxes that meet all criteria\n    mask = (\n        (denormalized_box_areas >= epsilon)\n        & (clipped_box_areas >= min_area - epsilon)\n        & (clipped_box_areas / denormalized_box_areas >= min_visibility - epsilon)\n        & (clipped_widths >= min_width - epsilon)\n        & (clipped_heights >= min_height - epsilon)\n    )\n\n    # Apply the mask to get the filtered bboxes\n    filtered_bboxes = clipped_bboxes[mask]\n\n    return np.array([], dtype=np.float32).reshape(0, 4) if len(filtered_bboxes) == 0 else filtered_bboxes\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.masks_from_bboxes","title":"def masks_from_bboxes (bboxes, shape) [view source on GitHub]","text":"

Create binary masks from multiple bounding boxes

Parameters:

Name Type Description bboxes np.ndarray

Array of bounding boxes with shape (N, 4), where N is the number of boxes

shape ShapeType | tuple[int, int]

{\"height\": int, \"width\": int} or tuple[int, int]

Returns:

Type Description masks

Array of binary masks with shape (N, height, width)

Source code in albumentations/core/bbox_utils.py Python
def masks_from_bboxes(bboxes: np.ndarray, shape: ShapeType | tuple[int, int]) -> np.ndarray:\n    \"\"\"Create binary masks from multiple bounding boxes\n\n    Args:\n        bboxes: Array of bounding boxes with shape (N, 4), where N is the number of boxes\n        shape: {\"height\": int, \"width\": int} or tuple[int, int]\n\n    Returns:\n        masks: Array of binary masks with shape (N, height, width)\n\n    \"\"\"\n    if isinstance(shape, dict):\n        height, width = shape[\"height\"], shape[\"width\"]\n    else:\n        height, width = shape[:2]\n\n    masks = np.zeros((len(bboxes), height, width), dtype=np.uint8)\n    y, x = np.ogrid[:height, :width]\n\n    for i, (x_min, y_min, x_max, y_max) in enumerate(bboxes[:, :4].astype(int)):\n        masks[i] = (x_min <= x) & (x < x_max) & (y_min <= y) & (y < y_max)\n\n    return masks\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.normalize_bboxes","title":"def normalize_bboxes (bboxes, shape) [view source on GitHub]","text":"

Normalize array of bounding boxes.

Parameters:

Name Type Description bboxes np.ndarray

Denormalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

shape ShapeType | tuple[int, int]

Image shape (height, width).

Returns:

Type Description np.ndarray

Normalized bounding boxes [(x_min, y_min, x_max, y_max, ...)].

Source code in albumentations/core/bbox_utils.py Python
@handle_empty_array(\"bboxes\")\ndef normalize_bboxes(bboxes: np.ndarray, shape: ShapeType | tuple[int, int]) -> np.ndarray:\n    \"\"\"Normalize array of bounding boxes.\n\n    Args:\n        bboxes: Denormalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n        shape: Image shape `(height, width)`.\n\n    Returns:\n        Normalized bounding boxes `[(x_min, y_min, x_max, y_max, ...)]`.\n\n    \"\"\"\n    if isinstance(shape, tuple):\n        rows, cols = shape[:2]\n    else:\n        rows, cols = shape[\"height\"], shape[\"width\"]\n\n    normalized = bboxes.copy().astype(float)\n    normalized[:, [0, 2]] /= cols\n    normalized[:, [1, 3]] /= rows\n    return normalized\n
"},{"location":"api_reference/core/bbox_utils/#albumentations.core.bbox_utils.union_of_bboxes","title":"def union_of_bboxes (bboxes, erosion_rate) [view source on GitHub]","text":"

Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.

Parameters:

Name Type Description bboxes np.ndarray

List of bounding boxes

erosion_rate float

How much each bounding box can be shrunk, useful for erosive cropping. Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.

Returns:

Type Description np.ndarray | None

A bounding box (x_min, y_min, x_max, y_max) or None if no bboxes are given or if the bounding boxes become invalid after erosion.

Source code in albumentations/core/bbox_utils.py Python
def union_of_bboxes(bboxes: np.ndarray, erosion_rate: float) -> np.ndarray | None:\n    \"\"\"Calculate union of bounding boxes. Boxes could be in albumentations or Pascal Voc format.\n\n    Args:\n        bboxes (np.ndarray): List of bounding boxes\n        erosion_rate (float): How much each bounding box can be shrunk, useful for erosive cropping.\n            Set this in range [0, 1]. 0 will not be erosive at all, 1.0 can make any bbox lose its volume.\n\n    Returns:\n        np.ndarray | None: A bounding box `(x_min, y_min, x_max, y_max)` or None if no bboxes are given or if\n                    the bounding boxes become invalid after erosion.\n    \"\"\"\n    if not bboxes.size:\n        return None\n\n    if erosion_rate == 1:\n        return None\n\n    if bboxes.shape[0] == 1:\n        return bboxes[0][:4]\n\n    epsilon = 1e-6\n\n    x_min, y_min = np.min(bboxes[:, :2], axis=0)\n    x_max, y_max = np.max(bboxes[:, 2:4], axis=0)\n\n    width = x_max - x_min\n    height = y_max - y_min\n\n    erosion_x = width * erosion_rate * 0.5\n    erosion_y = height * erosion_rate * 0.5\n\n    x_min += erosion_x\n    y_min += erosion_y\n    x_max -= erosion_x\n    y_max -= erosion_y\n\n    if abs(x_max - x_min) < epsilon or abs(y_max - y_min) < epsilon:\n        return None\n\n    return np.array([x_min, y_min, x_max, y_max], dtype=np.float32)\n
"},{"location":"api_reference/core/composition/","title":"Composition API (core.composition)","text":""},{"location":"api_reference/core/composition/#albumentations.core.composition.BaseCompose","title":"class BaseCompose (transforms, p, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub]","text":"

Base class for composing multiple transforms together.

This class serves as a foundation for creating compositions of transforms in the Albumentations library. It provides basic functionality for managing a sequence of transforms and applying them to data.

Attributes:

Name Type Description transforms List[TransformType]

A list of transforms to be applied.

p float

Probability of applying the compose. Should be in the range [0, 1].

replay_mode bool

If True, the compose is in replay mode.

_additional_targets Dict[str, str]

Additional targets for transforms.

_available_keys Set[str]

Set of available keys for data.

processors Dict[str, Union[BboxProcessor, KeypointsProcessor]]

Processors for specific data types.

Parameters:

Name Type Description transforms TransformsSeqType

A sequence of transforms to compose.

p float

Probability of applying the compose.

Exceptions:

Type Description ValueError

If an invalid additional target is specified.

Note

  • Subclasses should implement the call method to define how the composition is applied to data.
  • The class supports serialization and deserialization of transforms.
  • It provides methods for adding targets, setting deterministic behavior, and checking data validity post-transform.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class BaseCompose(Serializable):\n    \"\"\"Base class for composing multiple transforms together.\n\n    This class serves as a foundation for creating compositions of transforms\n    in the Albumentations library. It provides basic functionality for\n    managing a sequence of transforms and applying them to data.\n\n    Attributes:\n        transforms (List[TransformType]): A list of transforms to be applied.\n        p (float): Probability of applying the compose. Should be in the range [0, 1].\n        replay_mode (bool): If True, the compose is in replay mode.\n        _additional_targets (Dict[str, str]): Additional targets for transforms.\n        _available_keys (Set[str]): Set of available keys for data.\n        processors (Dict[str, Union[BboxProcessor, KeypointsProcessor]]): Processors for specific data types.\n\n    Args:\n        transforms (TransformsSeqType): A sequence of transforms to compose.\n        p (float): Probability of applying the compose.\n\n    Raises:\n        ValueError: If an invalid additional target is specified.\n\n    Note:\n        - Subclasses should implement the __call__ method to define how\n          the composition is applied to data.\n        - The class supports serialization and deserialization of transforms.\n        - It provides methods for adding targets, setting deterministic behavior,\n          and checking data validity post-transform.\n    \"\"\"\n\n    _transforms_dict: dict[int, BasicTransform] | None = None\n    check_each_transform: tuple[DataProcessor, ...] | None = None\n    main_compose: bool = True\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        p: float,\n        mask_interpolation: int | None = None,\n        seed: int | None = None,\n        save_applied_params: bool = False,\n    ):\n        if isinstance(transforms, (BaseCompose, BasicTransform)):\n            warnings.warn(\n                \"transforms is single transform, but a sequence is expected! Transform will be wrapped into list.\",\n                stacklevel=2,\n            )\n            transforms = [transforms]\n\n        self.transforms = transforms\n        self.p = p\n\n        self.replay_mode = False\n        self._additional_targets: dict[str, str] = {}\n        self._available_keys: set[str] = set()\n        self.processors: dict[str, BboxProcessor | KeypointsProcessor] = {}\n        self._set_keys()\n        self.set_mask_interpolation(mask_interpolation)\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n        self.set_random_seed(seed)\n        self.save_applied_params = save_applied_params\n\n    def _track_transform_params(self, transform: TransformType, data: dict[str, Any]) -> None:\n        \"\"\"Track transform parameters if tracking is enabled.\"\"\"\n        if \"applied_transforms\" in data and hasattr(transform, \"params\") and transform.params:\n            data[\"applied_transforms\"].append((transform.__class__.__name__, transform.params.copy()))\n\n    def set_random_state(\n        self,\n        random_generator: np.random.Generator,\n        py_random: random.Random,\n    ) -> None:\n        \"\"\"Set random state directly from generators.\n\n        Args:\n            random_generator: numpy random generator to use\n            py_random: python random generator to use\n        \"\"\"\n        self.random_generator = random_generator\n        self.py_random = py_random\n\n        # Propagate both random states to all transforms\n        for transform in self.transforms:\n            if isinstance(transform, (BasicTransform, BaseCompose)):\n                transform.set_random_state(random_generator, py_random)\n\n    def set_random_seed(self, seed: int | None) -> None:\n        \"\"\"Set random state from seed.\n\n        Args:\n            seed: Random seed to use\n        \"\"\"\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n\n        # Propagate seed to all transforms\n        for transform in self.transforms:\n            if isinstance(transform, (BasicTransform, BaseCompose)):\n                transform.set_random_seed(seed)\n\n    def set_mask_interpolation(self, mask_interpolation: int | None) -> None:\n        self.mask_interpolation = mask_interpolation\n        self._set_mask_interpolation_recursive(self.transforms)\n\n    def _set_mask_interpolation_recursive(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BasicTransform):\n                if hasattr(transform, \"mask_interpolation\") and self.mask_interpolation is not None:\n                    transform.mask_interpolation = self.mask_interpolation\n            elif isinstance(transform, BaseCompose):\n                transform.set_mask_interpolation(self.mask_interpolation)\n\n    def __iter__(self) -> Iterator[TransformType]:\n        return iter(self.transforms)\n\n    def __len__(self) -> int:\n        return len(self.transforms)\n\n    def __call__(self, *args: Any, **data: Any) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def __getitem__(self, item: int) -> TransformType:\n        return self.transforms[item]\n\n    def __repr__(self) -> str:\n        return self.indented_repr()\n\n    @property\n    def additional_targets(self) -> dict[str, str]:\n        return self._additional_targets\n\n    @property\n    def available_keys(self) -> set[str]:\n        return self._available_keys\n\n    def indented_repr(self, indent: int = REPR_INDENT_STEP) -> str:\n        args = {k: v for k, v in self.to_dict_private().items() if not (k.startswith(\"__\") or k == \"transforms\")}\n        repr_string = self.__class__.__name__ + \"([\"\n        for t in self.transforms:\n            repr_string += \"\\n\"\n            t_repr = t.indented_repr(indent + REPR_INDENT_STEP) if hasattr(t, \"indented_repr\") else repr(t)\n            repr_string += \" \" * indent + t_repr + \",\"\n        repr_string += \"\\n\" + \" \" * (indent - REPR_INDENT_STEP) + f\"], {format_args(args)})\"\n        return repr_string\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    def to_dict_private(self) -> dict[str, Any]:\n        return {\n            \"__class_fullname__\": self.get_class_fullname(),\n            \"p\": self.p,\n            \"transforms\": [t.to_dict_private() for t in self.transforms],\n        }\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        return {\n            \"__class_fullname__\": self.get_class_fullname(),\n            \"id\": id(self),\n            \"params\": None,\n            \"transforms\": [t.get_dict_with_id() for t in self.transforms],\n        }\n\n    def add_targets(self, additional_targets: dict[str, str] | None) -> None:\n        if additional_targets:\n            for k, v in additional_targets.items():\n                if k in self._additional_targets and v != self._additional_targets[k]:\n                    raise ValueError(\n                        f\"Trying to overwrite existed additional targets. \"\n                        f\"Key={k} Exists={self._additional_targets[k]} New value: {v}\",\n                    )\n            self._additional_targets.update(additional_targets)\n            for t in self.transforms:\n                t.add_targets(additional_targets)\n            for proc in self.processors.values():\n                proc.add_targets(additional_targets)\n        self._set_keys()\n\n    def _set_keys(self) -> None:\n        \"\"\"Set _available_keys\"\"\"\n        self._available_keys.update(self._additional_targets.keys())\n        for t in self.transforms:\n            self._available_keys.update(t.available_keys)\n            if hasattr(t, \"targets_as_params\"):\n                self._available_keys.update(t.targets_as_params)\n        if self.processors:\n            self._available_keys.update([\"labels\"])\n            for proc in self.processors.values():\n                if proc.default_data_name not in self._available_keys:  # if no transform to process this data\n                    warnings.warn(\n                        f\"Got processor for {proc.default_data_name}, but no transform to process it.\",\n                        stacklevel=2,\n                    )\n                self._available_keys.update(proc.data_fields)\n                if proc.params.label_fields:\n                    self._available_keys.update(proc.params.label_fields)\n\n    def set_deterministic(self, flag: bool, save_key: str = \"replay\") -> None:\n        for t in self.transforms:\n            t.set_deterministic(flag, save_key)\n\n    def check_data_post_transform(self, data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Check and filter data after transformation.\n\n        Args:\n            data: Dictionary containing transformed data\n\n        Returns:\n            Filtered data dictionary\n        \"\"\"\n        if self.check_each_transform:\n            shape = get_shape(data)\n\n            for proc in self.check_each_transform:\n                for data_name, data_value in data.items():\n                    if data_name in proc.data_fields or (\n                        data_name in self._additional_targets\n                        and self._additional_targets[data_name] in proc.data_fields\n                    ):\n                        data[data_name] = proc.filter(data_value, shape)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.Compose","title":"class Compose (transforms, bbox_params=None, keypoint_params=None, additional_targets=None, p=1.0, is_check_shapes=True, strict=True, mask_interpolation=None, seed=None, save_applied_params=False) [view source on GitHub]","text":"

Compose multiple transforms together and apply them sequentially to input data.

This class allows you to chain multiple image augmentation transforms and apply them in a specified order. It also handles bounding box and keypoint transformations if the appropriate parameters are provided.

Parameters:

Name Type Description transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to apply.

bbox_params Union[dict, BboxParams, None]

Parameters for bounding box transforms. Can be a dict of params or a BboxParams object. Default is None.

keypoint_params Union[dict, KeypointParams, None]

Parameters for keypoint transforms. Can be a dict of params or a KeypointParams object. Default is None.

additional_targets Dict[str, str]

A dictionary mapping additional target names to their types. For example, {'image2': 'image'}. Default is None.

p float

Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.

is_check_shapes bool

If True, checks consistency of shapes for image/mask/masks on each call. Disable only if you are sure about your data consistency. Default is True.

strict bool

If True, raises an error on unknown input keys. If False, ignores them. Default is True.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

seed int

Random seed. Default is None.

save_applied_params bool

If True, saves the applied parameters of each transform. Default is False.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n...     A.RandomCrop(width=256, height=256),\n...     A.HorizontalFlip(p=0.5),\n...     A.RandomBrightnessContrast(p=0.2),\n... ])\n>>> transformed = transform(image=image)\n

Note

  • The class checks the validity of input data and shapes if is_check_args and is_check_shapes are True.
  • When bbox_params or keypoint_params are provided, it sets up the corresponding processors.
  • The transform can handle additional targets specified in the additional_targets dictionary.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class Compose(BaseCompose, HubMixin):\n    \"\"\"Compose multiple transforms together and apply them sequentially to input data.\n\n    This class allows you to chain multiple image augmentation transforms and apply them\n    in a specified order. It also handles bounding box and keypoint transformations if\n    the appropriate parameters are provided.\n\n    Args:\n        transforms (List[Union[BasicTransform, BaseCompose]]): A list of transforms to apply.\n        bbox_params (Union[dict, BboxParams, None]): Parameters for bounding box transforms.\n            Can be a dict of params or a BboxParams object. Default is None.\n        keypoint_params (Union[dict, KeypointParams, None]): Parameters for keypoint transforms.\n            Can be a dict of params or a KeypointParams object. Default is None.\n        additional_targets (Dict[str, str], optional): A dictionary mapping additional target names\n            to their types. For example, {'image2': 'image'}. Default is None.\n        p (float): Probability of applying all transforms. Should be in range [0, 1]. Default is 1.0.\n        is_check_shapes (bool): If True, checks consistency of shapes for image/mask/masks on each call.\n            Disable only if you are sure about your data consistency. Default is True.\n        strict (bool): If True, raises an error on unknown input keys. If False, ignores them. Default is True.\n        mask_interpolation (int, optional): Interpolation method for mask transforms. When defined,\n            it overrides the interpolation method specified in individual transforms. Default is None.\n        seed (int, optional): Random seed. Default is None.\n        save_applied_params (bool): If True, saves the applied parameters of each transform. Default is False.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        ...     A.RandomCrop(width=256, height=256),\n        ...     A.HorizontalFlip(p=0.5),\n        ...     A.RandomBrightnessContrast(p=0.2),\n        ... ])\n        >>> transformed = transform(image=image)\n\n    Note:\n        - The class checks the validity of input data and shapes if is_check_args and is_check_shapes are True.\n        - When bbox_params or keypoint_params are provided, it sets up the corresponding processors.\n        - The transform can handle additional targets specified in the additional_targets dictionary.\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        bbox_params: dict[str, Any] | BboxParams | None = None,\n        keypoint_params: dict[str, Any] | KeypointParams | None = None,\n        additional_targets: dict[str, str] | None = None,\n        p: float = 1.0,\n        is_check_shapes: bool = True,\n        strict: bool = True,\n        mask_interpolation: int | None = None,\n        seed: int | None = None,\n        save_applied_params: bool = False,\n    ):\n        super().__init__(\n            transforms=transforms,\n            p=p,\n            mask_interpolation=mask_interpolation,\n            seed=seed,\n            save_applied_params=save_applied_params,\n        )\n\n        if bbox_params:\n            if isinstance(bbox_params, dict):\n                b_params = BboxParams(**bbox_params)\n            elif isinstance(bbox_params, BboxParams):\n                b_params = bbox_params\n            else:\n                msg = \"unknown format of bbox_params, please use `dict` or `BboxParams`\"\n                raise ValueError(msg)\n            self.processors[\"bboxes\"] = BboxProcessor(b_params)\n\n        if keypoint_params:\n            if isinstance(keypoint_params, dict):\n                k_params = KeypointParams(**keypoint_params)\n            elif isinstance(keypoint_params, KeypointParams):\n                k_params = keypoint_params\n            else:\n                msg = \"unknown format of keypoint_params, please use `dict` or `KeypointParams`\"\n                raise ValueError(msg)\n            self.processors[\"keypoints\"] = KeypointsProcessor(k_params)\n\n        for proc in self.processors.values():\n            proc.ensure_transforms_valid(self.transforms)\n\n        self.add_targets(additional_targets)\n        if not self.transforms:  # if no transforms -> do nothing, all keys will be available\n            self._available_keys.update(AVAILABLE_KEYS)\n\n        self.is_check_args = True\n        self.strict = strict\n\n        self.is_check_shapes = is_check_shapes\n        self.check_each_transform = tuple(  # processors that checks after each transform\n            proc for proc in self.processors.values() if getattr(proc.params, \"check_each_transform\", False)\n        )\n        self._set_check_args_for_transforms(self.transforms)\n\n        self._set_processors_for_transforms(self.transforms)\n\n        self.save_applied_params = save_applied_params\n        self._images_was_list = False\n        self._masks_was_list = False\n\n    def _set_processors_for_transforms(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BasicTransform):\n                if hasattr(transform, \"set_processors\"):\n                    transform.set_processors(self.processors)\n            elif isinstance(transform, BaseCompose):\n                self._set_processors_for_transforms(transform.transforms)\n\n    def _set_check_args_for_transforms(self, transforms: TransformsSeqType) -> None:\n        for transform in transforms:\n            if isinstance(transform, BaseCompose):\n                self._set_check_args_for_transforms(transform.transforms)\n                transform.check_each_transform = self.check_each_transform\n                transform.processors = self.processors\n            if isinstance(transform, Compose):\n                transform.disable_check_args_private()\n\n    def disable_check_args_private(self) -> None:\n        self.is_check_args = False\n        self.strict = False\n        self.main_compose = False\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if args:\n            msg = \"You have to pass data to augmentations as named arguments, for example: aug(image=image)\"\n            raise KeyError(msg)\n\n        if not isinstance(force_apply, (bool, int)):\n            msg = \"force_apply must have bool or int type\"\n            raise TypeError(msg)\n\n        # Initialize applied_transforms only in top-level Compose if requested\n        if self.save_applied_params and self.main_compose:\n            data[\"applied_transforms\"] = []\n\n        need_to_run = force_apply or self.py_random.random() < self.p\n        if not need_to_run:\n            return data\n\n        self.preprocess(data)\n\n        for t in self.transforms:\n            data = t(**data)\n            self._track_transform_params(t, data)\n            data = self.check_data_post_transform(data)\n\n        return self.postprocess(data)\n\n    def preprocess(self, data: Any) -> None:\n        \"\"\"Preprocess input data before applying transforms.\"\"\"\n        self._validate_data(data)\n        self._preprocess_processors(data)\n        self._preprocess_arrays(data)\n\n    def _validate_data(self, data: dict[str, Any]) -> None:\n        \"\"\"Validate input data keys and arguments.\"\"\"\n        if not self.strict:\n            return\n\n        for data_name in data:\n            if not self._is_valid_key(data_name):\n                raise ValueError(f\"Key {data_name} is not in available keys.\")\n\n        if self.is_check_args:\n            self._check_args(**data)\n\n    def _is_valid_key(self, key: str) -> bool:\n        \"\"\"Check if the key is valid for processing.\"\"\"\n        return key in self._available_keys or key in MASK_KEYS or key in IMAGE_KEYS or key == \"applied_transforms\"\n\n    def _preprocess_processors(self, data: dict[str, Any]) -> None:\n        \"\"\"Run preprocessors if this is the main compose.\"\"\"\n        if not self.main_compose:\n            return\n\n        for processor in self.processors.values():\n            processor.ensure_data_valid(data)\n        for processor in self.processors.values():\n            processor.preprocess(data)\n\n    def _preprocess_arrays(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert lists to numpy arrays for images and masks.\"\"\"\n        self._preprocess_images(data)\n        self._preprocess_masks(data)\n\n    def _preprocess_images(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert image lists to numpy arrays.\"\"\"\n        if \"images\" not in data:\n            return\n\n        if isinstance(data[\"images\"], (list, tuple)):\n            self._images_was_list = True\n            data[\"images\"] = np.stack(data[\"images\"])\n        else:\n            self._images_was_list = False\n\n    def _preprocess_masks(self, data: dict[str, Any]) -> None:\n        \"\"\"Convert mask lists to numpy arrays.\"\"\"\n        if \"masks\" not in data:\n            return\n\n        if isinstance(data[\"masks\"], (list, tuple)):\n            self._masks_was_list = True\n            data[\"masks\"] = np.stack(data[\"masks\"])\n        else:\n            self._masks_was_list = False\n\n    def postprocess(self, data: dict[str, Any]) -> dict[str, Any]:\n        if self.main_compose:\n            for p in self.processors.values():\n                p.postprocess(data)\n\n            # Convert back to list if original input was a list\n            if \"images\" in data and self._images_was_list:\n                data[\"images\"] = list(data[\"images\"])\n\n            if \"masks\" in data and self._masks_was_list:\n                data[\"masks\"] = list(data[\"masks\"])\n\n        return data\n\n    def to_dict_private(self) -> dict[str, Any]:\n        dictionary = super().to_dict_private()\n        bbox_processor = self.processors.get(\"bboxes\")\n        keypoints_processor = self.processors.get(\"keypoints\")\n        dictionary.update(\n            {\n                \"bbox_params\": bbox_processor.params.to_dict_private() if bbox_processor else None,\n                \"keypoint_params\": (keypoints_processor.params.to_dict_private() if keypoints_processor else None),\n                \"additional_targets\": self.additional_targets,\n                \"is_check_shapes\": self.is_check_shapes,\n            },\n        )\n        return dictionary\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        dictionary = super().get_dict_with_id()\n        bbox_processor = self.processors.get(\"bboxes\")\n        keypoints_processor = self.processors.get(\"keypoints\")\n        dictionary.update(\n            {\n                \"bbox_params\": bbox_processor.params.to_dict_private() if bbox_processor else None,\n                \"keypoint_params\": (keypoints_processor.params.to_dict_private() if keypoints_processor else None),\n                \"additional_targets\": self.additional_targets,\n                \"params\": None,\n                \"is_check_shapes\": self.is_check_shapes,\n            },\n        )\n        return dictionary\n\n    @staticmethod\n    def _check_single_data(data_name: str, data: Any) -> tuple[int, int]:\n        if not isinstance(data, np.ndarray):\n            raise TypeError(f\"{data_name} must be numpy array type\")\n        return data.shape[:2]\n\n    @staticmethod\n    def _check_masks_data(data_name: str, data: Any) -> tuple[int, int]:\n        \"\"\"Check masks data format and return shape.\n\n        Args:\n            data_name: Name of the data field being checked\n            data: Input data in one of these formats:\n                - List of numpy arrays, each of shape (H, W) or (H, W, C)\n                - Numpy array of shape (N, H, W) or (N, H, W, C)\n\n        Returns:\n            tuple: (height, width) of the first mask\n\n        Raises:\n            TypeError: If data format is invalid\n        \"\"\"\n        if isinstance(data, np.ndarray):\n            if data.ndim not in [3, 4]:  # (N,H,W) or (N,H,W,C)\n                raise TypeError(f\"{data_name} as numpy array must be 3D or 4D\")\n            return data.shape[1:3]  # Return (H,W)\n\n        if isinstance(data, (list, tuple)):\n            if not data:\n                raise ValueError(f\"{data_name} cannot be empty\")\n            if not all(isinstance(m, np.ndarray) for m in data):\n                raise TypeError(f\"All elements in {data_name} must be numpy arrays\")\n            if any(m.ndim not in [2, 3] for m in data):\n                raise TypeError(f\"All masks in {data_name} must be 2D or 3D numpy arrays\")\n            return data[0].shape[:2]\n\n        raise TypeError(f\"{data_name} must be either a numpy array or a sequence of numpy arrays\")\n\n    @staticmethod\n    def _check_multi_data(data_name: str, data: Any) -> tuple[int, int]:\n        \"\"\"Check multi-image data format and return shape.\n\n        Args:\n            data_name: Name of the data field being checked\n            data: Input data in one of these formats:\n                - List-like of numpy arrays\n                - Numpy array of shape (N, H, W, C) or (N, H, W)\n\n        Returns:\n            tuple: (height, width) of the first image\n\n        Raises:\n            TypeError: If data format is invalid\n        \"\"\"\n        if isinstance(data, np.ndarray):\n            if data.ndim not in {3, 4}:  # (N,H,W) or (N,H,W,C)\n                raise TypeError(f\"{data_name} as numpy array must be 3D or 4D\")\n            return data.shape[1:3]  # Return (H,W)\n\n        if not isinstance(data, Sequence) or not isinstance(data[0], np.ndarray):\n            raise TypeError(f\"{data_name} must be either a numpy array or a list of numpy arrays\")\n        return data[0].shape[:2]\n\n    @staticmethod\n    def _check_bbox_keypoint_params(internal_data_name: str, processors: dict[str, Any]) -> None:\n        if internal_data_name in CHECK_BBOX_PARAM and processors.get(\"bboxes\") is None:\n            raise ValueError(\"bbox_params must be specified for bbox transformations\")\n        if internal_data_name in CHECK_KEYPOINTS_PARAM and processors.get(\"keypoints\") is None:\n            raise ValueError(\"keypoints_params must be specified for keypoint transformations\")\n\n    @staticmethod\n    def _check_shapes(shapes: list[tuple[int, ...]], is_check_shapes: bool) -> None:\n        if is_check_shapes and shapes and shapes.count(shapes[0]) != len(shapes):\n            raise ValueError(\n                \"Height and Width of image, mask or masks should be equal. You can disable shapes check \"\n                \"by setting a parameter is_check_shapes=False of Compose class (do it only if you are sure \"\n                \"about your data consistency).\",\n            )\n\n    def _check_args(self, **kwargs: Any) -> None:\n        shapes = []  # For H,W checks\n        volume_shapes = []  # For D,H,W checks\n\n        for data_name, data in kwargs.items():\n            internal_name = self._additional_targets.get(data_name, data_name)\n\n            # For CHECKED_SINGLE, we must validate even if None\n            if internal_name in CHECKED_SINGLE:\n                if not isinstance(data, np.ndarray):\n                    raise TypeError(f\"{data_name} must be numpy array type\")\n                shapes.append(data.shape[:2])\n                continue\n\n            # Skip empty data or non-array/list inputs for other types\n            if data is None:\n                continue\n            if not isinstance(data, (np.ndarray, list)):\n                continue\n\n            self._check_bbox_keypoint_params(internal_name, self.processors)\n\n            shape = self._get_data_shape(data_name, internal_name, data)\n            if shape is None:\n                continue\n\n            # Handle different shape types\n            if internal_name in CHECKED_VOLUME | CHECKED_MASK3D:\n                shapes.append(shape[1:3])  # H,W from (D,H,W)\n                volume_shapes.append(shape[:3])  # D,H,W\n            elif internal_name in {\"volumes\", \"masks3d\"}:\n                shapes.append(shape[2:4])  # H,W from (N,D,H,W)\n                volume_shapes.append(shape[1:4])  # D,H,W from (N,D,H,W)\n            else:\n                shapes.append(shape[:2])  # H,W\n\n        self._check_shape_consistency(shapes, volume_shapes)\n\n    def _get_data_shape(self, data_name: str, internal_name: str, data: Any) -> tuple[int, ...] | None:\n        \"\"\"Get shape of data based on its type.\"\"\"\n        if internal_name in CHECKED_SINGLE:\n            if not isinstance(data, np.ndarray):\n                raise TypeError(f\"{data_name} must be numpy array type\")\n            return data.shape\n\n        if internal_name in CHECKED_VOLUME:\n            return self._check_volume_data(data_name, data)\n\n        if internal_name in CHECKED_MASK3D:\n            return self._check_mask3d_data(data_name, data)\n\n        if internal_name in CHECKED_MULTI:\n            if internal_name == \"masks\":\n                return self._check_masks_data(data_name, data)\n            if internal_name in {\"volumes\", \"masks3d\"}:  # Group these together\n                if not isinstance(data, np.ndarray):\n                    raise TypeError(f\"{data_name} must be numpy array type\")\n                if data.ndim not in {4, 5}:  # (N,D,H,W) or (N,D,H,W,C)\n                    raise TypeError(f\"{data_name} must be 4D or 5D array\")\n                return data.shape  # Return full shape\n            return self._check_multi_data(data_name, data)\n\n        return None\n\n    def _check_shape_consistency(self, shapes: list[tuple[int, ...]], volume_shapes: list[tuple[int, ...]]) -> None:\n        \"\"\"Check consistency of shapes.\"\"\"\n        # Check H,W consistency\n        self._check_shapes(shapes, self.is_check_shapes)\n\n        # Check D,H,W consistency for volumes and 3D masks\n        if self.is_check_shapes and volume_shapes and volume_shapes.count(volume_shapes[0]) != len(volume_shapes):\n            raise ValueError(\n                \"Depth, Height and Width of volume, mask3d, volumes and masks3d should be equal. \"\n                \"You can disable shapes check by setting is_check_shapes=False.\",\n            )\n\n    @staticmethod\n    def _check_volume_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        if data.ndim not in {3, 4}:  # (D,H,W) or (D,H,W,C)\n            raise TypeError(f\"{data_name} must be 3D or 4D array\")\n        return data.shape[:3]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_volumes_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        if data.ndim not in {4, 5}:  # (N,D,H,W) or (N,D,H,W,C)\n            raise TypeError(f\"{data_name} must be 4D or 5D array\")\n        return data.shape[1:4]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_mask3d_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        \"\"\"Check single volumetric mask data format and return shape.\"\"\"\n        if data.ndim not in {3, 4}:  # (D,H,W) or (D,H,W,C)\n            raise TypeError(f\"{data_name} must be 3D or 4D array\")\n        return data.shape[:3]  # Return (D,H,W)\n\n    @staticmethod\n    def _check_masks3d_data(data_name: str, data: np.ndarray) -> tuple[int, int, int]:\n        \"\"\"Check multiple volumetric masks data format and return shape.\"\"\"\n        if data.ndim not in [4, 5]:  # (N,D,H,W) or (N,D,H,W,C)\n            raise TypeError(f\"{data_name} must be 4D or 5D array\")\n        return data.shape[1:4]  # Return (D,H,W)\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.OneOf","title":"class OneOf (transforms, p=0.5) [view source on GitHub]","text":"

Select one of transforms to apply. Selected transform will be called with force_apply=True. Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.

Parameters:

Name Type Description transforms list

list of transformations to compose.

p float

probability of applying selected transform. Default: 0.5.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class OneOf(BaseCompose):\n    \"\"\"Select one of transforms to apply. Selected transform will be called with `force_apply=True`.\n    Transforms probabilities will be normalized to one 1, so in this case transforms probabilities works as weights.\n\n    Args:\n        transforms (list): list of transformations to compose.\n        p (float): probability of applying selected transform. Default: 0.5.\n\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, p: float = 0.5):\n        super().__init__(transforms=transforms, p=p)\n        transforms_ps = [t.p for t in self.transforms]\n        s = sum(transforms_ps)\n        self.transforms_ps = [t / s for t in transforms_ps]\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n            return data\n\n        if self.transforms_ps and (force_apply or self.py_random.random() < self.p):\n            idx: int = self.random_generator.choice(len(self.transforms), p=self.transforms_ps)\n            t = self.transforms[idx]\n            data = t(force_apply=True, **data)\n            self._track_transform_params(t, data)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.OneOrOther","title":"class OneOrOther (first=None, second=None, transforms=None, p=0.5) [view source on GitHub]","text":"

Select one or another transform to apply. Selected transform will be called with force_apply=True.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class OneOrOther(BaseCompose):\n    \"\"\"Select one or another transform to apply. Selected transform will be called with `force_apply=True`.\"\"\"\n\n    def __init__(\n        self,\n        first: TransformType | None = None,\n        second: TransformType | None = None,\n        transforms: TransformsSeqType | None = None,\n        p: float = 0.5,\n    ):\n        if transforms is None:\n            if first is None or second is None:\n                msg = \"You must set both first and second or set transforms argument.\"\n                raise ValueError(msg)\n            transforms = [first, second]\n        super().__init__(transforms, p)\n        if len(self.transforms) != NUM_ONEOF_TRANSFORMS:\n            warnings.warn(\"Length of transforms is not equal to 2.\", stacklevel=2)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n                self._track_transform_params(t, data)\n            return data\n\n        if self.py_random.random() < self.p:\n            return self.transforms[0](force_apply=True, **data)\n\n        return self.transforms[-1](force_apply=True, **data)\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.RandomOrder","title":"class RandomOrder (transforms, n=1, replace=False, p=1) [view source on GitHub]","text":"

Apply a random subset of transforms from the given list in a random order.

The RandomOrder class allows you to select a specified number of transforms from a list and apply them to the input data in a random order. This is useful for creating more diverse augmentation pipelines where the order of transformations can vary, potentially leading to different results.

Attributes:

Name Type Description transforms TransformsSeqType

A list of transformations to choose from.

n int

The number of transforms to apply. If n is greater than the number of available transforms and replace is False, n will be set to the number of available transforms.

replace bool

Whether to sample transforms with replacement. If True, the same transform can be selected multiple times. Default is False.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.RandomOrder([\n...     A.HorizontalFlip(p=1),\n...     A.VerticalFlip(p=1),\n...     A.RandomBrightnessContrast(p=1),\n... ], n=2, replace=False, p=0.5)\n>>> # This will apply 2 out of the 3 transforms in a random order with 50% probability\n

Note

  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.
  • The random order of transforms will not be replayed in ReplayCompose.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class RandomOrder(SomeOf):\n    \"\"\"Apply a random subset of transforms from the given list in a random order.\n\n    The `RandomOrder` class allows you to select a specified number of transforms from a list and apply them\n    to the input data in a random order. This is useful for creating more diverse augmentation pipelines\n    where the order of transformations can vary, potentially leading to different results.\n\n    Attributes:\n        transforms (TransformsSeqType): A list of transformations to choose from.\n        n (int): The number of transforms to apply. If `n` is greater than the number of available transforms\n                 and `replace` is False, `n` will be set to the number of available transforms.\n        replace (bool): Whether to sample transforms with replacement. If True, the same transform can be\n                        selected multiple times. Default is False.\n        p (float): Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.RandomOrder([\n        ...     A.HorizontalFlip(p=1),\n        ...     A.VerticalFlip(p=1),\n        ...     A.RandomBrightnessContrast(p=1),\n        ... ], n=2, replace=False, p=0.5)\n        >>> # This will apply 2 out of the 3 transforms in a random order with 50% probability\n\n    Note:\n        - The probabilities of individual transforms are used as weights for sampling.\n        - When `replace` is True, the same transform can be selected multiple times.\n        - The random order of transforms will not be replayed in `ReplayCompose`.\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, n: int = 1, replace: bool = False, p: float = 1):\n        super().__init__(transforms=transforms, n=n, replace=replace, p=p)\n\n    def _get_idx(self) -> np.ndarray[np.int_]:\n        return self.random_generator.choice(\n            len(self.transforms),\n            size=self.n,\n            replace=self.replace,\n            p=self.transforms_ps,\n        )\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.SelectiveChannelTransform","title":"class SelectiveChannelTransform (transforms, channels=(0, 1, 2), p=1.0) [view source on GitHub]","text":"

A transformation class to apply specified transforms to selected channels of an image.

This class extends BaseCompose to allow selective application of transformations to specified image channels. It extracts the selected channels, applies the transformations, and then reinserts the transformed channels back into their original positions in the image.

Parameters:

Name Type Description transforms TransformsSeqType

A sequence of transformations (from Albumentations) to be applied to the specified channels.

channels Sequence[int]

A sequence of integers specifying the indices of the channels to which the transforms should be applied.

p float

Probability that the transform will be applied; the default is 1.0 (always apply).

Methods

call(args, *kwargs): Applies the transforms to the image according to the specified channels. The input data should include 'image' key with the image array.

Returns:

Type Description dict[str, Any]

The transformed data dictionary, which includes the transformed 'image' key.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class SelectiveChannelTransform(BaseCompose):\n    \"\"\"A transformation class to apply specified transforms to selected channels of an image.\n\n    This class extends BaseCompose to allow selective application of transformations to\n    specified image channels. It extracts the selected channels, applies the transformations,\n    and then reinserts the transformed channels back into their original positions in the image.\n\n    Parameters:\n        transforms (TransformsSeqType):\n            A sequence of transformations (from Albumentations) to be applied to the specified channels.\n        channels (Sequence[int]):\n            A sequence of integers specifying the indices of the channels to which the transforms should be applied.\n        p (float):\n            Probability that the transform will be applied; the default is 1.0 (always apply).\n\n    Methods:\n        __call__(*args, **kwargs):\n            Applies the transforms to the image according to the specified channels.\n            The input data should include 'image' key with the image array.\n\n    Returns:\n        dict[str, Any]: The transformed data dictionary, which includes the transformed 'image' key.\n    \"\"\"\n\n    def __init__(\n        self,\n        transforms: TransformsSeqType,\n        channels: Sequence[int] = (0, 1, 2),\n        p: float = 1.0,\n    ) -> None:\n        super().__init__(transforms, p)\n        self.channels = channels\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if force_apply or self.py_random.random() < self.p:\n            image = data[\"image\"]\n\n            selected_channels = image[:, :, self.channels]\n            sub_image = np.ascontiguousarray(selected_channels)\n\n            for t in self.transforms:\n                sub_image = t(image=sub_image)[\"image\"]\n                self._track_transform_params(t, sub_image)\n\n            transformed_channels = cv2.split(sub_image)\n            output_img = image.copy()\n\n            for idx, channel in zip(self.channels, transformed_channels):\n                output_img[:, :, idx] = channel\n\n            data[\"image\"] = np.ascontiguousarray(output_img)\n\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.Sequential","title":"class Sequential (transforms, p=0.5) [view source on GitHub]","text":"

Sequentially applies all transforms to targets.

Note

This transform is not intended to be a replacement for Compose. Instead, it should be used inside Compose the same way OneOf or OneOrOther are used. For instance, you can combine OneOf with Sequential to create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly chose sequence to input data (see the Example section for an example definition of such pipeline).

Examples:

Python
>>> import albumentations as A\n>>> transform = A.Compose([\n>>>    A.OneOf([\n>>>        A.Sequential([\n>>>            A.HorizontalFlip(p=0.5),\n>>>            A.ShiftScaleRotate(p=0.5),\n>>>        ]),\n>>>        A.Sequential([\n>>>            A.VerticalFlip(p=0.5),\n>>>            A.RandomBrightnessContrast(p=0.5),\n>>>        ]),\n>>>    ], p=1)\n>>> ])\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class Sequential(BaseCompose):\n    \"\"\"Sequentially applies all transforms to targets.\n\n    Note:\n        This transform is not intended to be a replacement for `Compose`. Instead, it should be used inside `Compose`\n        the same way `OneOf` or `OneOrOther` are used. For instance, you can combine `OneOf` with `Sequential` to\n        create an augmentation pipeline that contains multiple sequences of augmentations and applies one randomly\n        chose sequence to input data (see the `Example` section for an example definition of such pipeline).\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.Compose([\n        >>>    A.OneOf([\n        >>>        A.Sequential([\n        >>>            A.HorizontalFlip(p=0.5),\n        >>>            A.ShiftScaleRotate(p=0.5),\n        >>>        ]),\n        >>>        A.Sequential([\n        >>>            A.VerticalFlip(p=0.5),\n        >>>            A.RandomBrightnessContrast(p=0.5),\n        >>>        ]),\n        >>>    ], p=1)\n        >>> ])\n\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, p: float = 0.5):\n        super().__init__(transforms, p)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode or force_apply or self.py_random.random() < self.p:\n            for t in self.transforms:\n                data = t(**data)\n                self._track_transform_params(t, data)\n                data = self.check_data_post_transform(data)\n        return data\n
"},{"location":"api_reference/core/composition/#albumentations.core.composition.SomeOf","title":"class SomeOf (transforms, n=1, replace=False, p=1) [view source on GitHub]","text":"

Apply a random subset of transforms from the given list.

This class selects a specified number of transforms from the provided list and applies them to the input data. The selection can be done with or without replacement, allowing for the same transform to be potentially applied multiple times.

Parameters:

Name Type Description transforms List[Union[BasicTransform, BaseCompose]]

A list of transforms to choose from.

n int

The number of transforms to apply. If greater than the number of transforms and replace=False, it will be set to the number of transforms.

replace bool

Whether to sample transforms with replacement. Default is True.

p float

Probability of applying the selected transforms. Should be in the range [0, 1]. Default is 1.0.

mask_interpolation int

Interpolation method for mask transforms. When defined, it overrides the interpolation method specified in individual transforms. Default is None.

Note

  • If n is greater than the number of transforms and replace is False, n will be set to the number of transforms with a warning.
  • The probabilities of individual transforms are used as weights for sampling.
  • When replace is True, the same transform can be selected multiple times.

Examples:

Python
>>> import albumentations as A\n>>> transform = A.SomeOf([\n...     A.HorizontalFlip(p=1),\n...     A.VerticalFlip(p=1),\n...     A.RandomBrightnessContrast(p=1),\n... ], n=2, replace=False, p=0.5)\n>>> # This will apply 2 out of the 3 transforms with 50% probability\n

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/composition.py Python
class SomeOf(BaseCompose):\n    \"\"\"Apply a random subset of transforms from the given list.\n\n    This class selects a specified number of transforms from the provided list\n    and applies them to the input data. The selection can be done with or without\n    replacement, allowing for the same transform to be potentially applied multiple times.\n\n    Args:\n        transforms (List[Union[BasicTransform, BaseCompose]]): A list of transforms to choose from.\n        n (int): The number of transforms to apply. If greater than the number of\n                 transforms and replace=False, it will be set to the number of transforms.\n        replace (bool): Whether to sample transforms with replacement. Default is True.\n        p (float): Probability of applying the selected transforms. Should be in the range [0, 1].\n                   Default is 1.0.\n        mask_interpolation (int, optional): Interpolation method for mask transforms.\n                                            When defined, it overrides the interpolation method\n                                            specified in individual transforms. Default is None.\n\n    Note:\n        - If `n` is greater than the number of transforms and `replace` is False,\n          `n` will be set to the number of transforms with a warning.\n        - The probabilities of individual transforms are used as weights for sampling.\n        - When `replace` is True, the same transform can be selected multiple times.\n\n    Example:\n        >>> import albumentations as A\n        >>> transform = A.SomeOf([\n        ...     A.HorizontalFlip(p=1),\n        ...     A.VerticalFlip(p=1),\n        ...     A.RandomBrightnessContrast(p=1),\n        ... ], n=2, replace=False, p=0.5)\n        >>> # This will apply 2 out of the 3 transforms with 50% probability\n    \"\"\"\n\n    def __init__(self, transforms: TransformsSeqType, n: int = 1, replace: bool = False, p: float = 1):\n        super().__init__(transforms, p)\n        self.n = n\n        if not replace and n > len(self.transforms):\n            self.n = len(self.transforms)\n            warnings.warn(\n                f\"`n` is greater than number of transforms. `n` will be set to {self.n}.\",\n                UserWarning,\n                stacklevel=2,\n            )\n        self.replace = replace\n        transforms_ps = [t.p for t in self.transforms]\n        s = sum(transforms_ps)\n        self.transforms_ps = [t / s for t in transforms_ps]\n\n    def __call__(self, *arg: Any, force_apply: bool = False, **data: Any) -> dict[str, Any]:\n        if self.replay_mode:\n            for t in self.transforms:\n                data = t(**data)\n                data = self.check_data_post_transform(data)\n            return data\n\n        if self.transforms_ps and (force_apply or self.py_random.random() < self.p):\n            for i in self._get_idx():\n                t = self.transforms[i]\n                data = t(force_apply=True, **data)\n                self._track_transform_params(t, data)\n                data = self.check_data_post_transform(data)\n        return data\n\n    def _get_idx(self) -> np.ndarray[np.int_]:\n        idx = self.random_generator.choice(\n            len(self.transforms),\n            size=self.n,\n            replace=self.replace,\n            p=self.transforms_ps,\n        )\n        idx.sort()\n        return idx\n\n    def to_dict_private(self) -> dict[str, Any]:\n        dictionary = super().to_dict_private()\n        dictionary.update({\"n\": self.n, \"replace\": self.replace})\n        return dictionary\n
"},{"location":"api_reference/core/keypoints_utils/","title":"Helper functions for working with keypoints (augmentations.core.keypoints_utils)","text":""},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.KeypointParams","title":"class KeypointParams (format, label_fields=None, remove_invisible=True, angle_in_degrees=True, check_each_transform=True) [view source on GitHub]","text":"

Parameters of keypoints

Parameters:

Name Type Description format str

format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa', 'xyz'.

x - X coordinate,

y - Y coordinate

z - Z coordinate (for 3D keypoints)

s - Keypoint scale

a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)

label_fields list

list of fields that are joined with keypoints, e.g labels. Should be same type as keypoints.

remove_invisible bool

to remove invisible points after transform or not

angle_in_degrees bool

angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints

check_each_transform bool

if True, then keypoints will be checked after each dual transform. Default: True

Note

The internal Albumentations format is [x, y, z, angle, scale]. For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0. For formats without angle or scale, these values are set to 0.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py Python
class KeypointParams(Params):\n    \"\"\"Parameters of keypoints\n\n    Args:\n        format (str): format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa', 'xyz'.\n\n            x - X coordinate,\n\n            y - Y coordinate\n\n            z - Z coordinate (for 3D keypoints)\n\n            s - Keypoint scale\n\n            a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)\n\n        label_fields (list): list of fields that are joined with keypoints, e.g labels.\n            Should be same type as keypoints.\n        remove_invisible (bool): to remove invisible points after transform or not\n        angle_in_degrees (bool): angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints\n        check_each_transform (bool): if `True`, then keypoints will be checked after each dual transform.\n            Default: `True`\n\n    Note:\n        The internal Albumentations format is [x, y, z, angle, scale]. For 2D formats (xy, yx, xya, xys, xyas, xysa),\n        z coordinate is set to 0. For formats without angle or scale, these values are set to 0.\n    \"\"\"\n\n    def __init__(\n        self,\n        format: str,  # noqa: A002\n        label_fields: Sequence[str] | None = None,\n        remove_invisible: bool = True,\n        angle_in_degrees: bool = True,\n        check_each_transform: bool = True,\n    ):\n        super().__init__(format, label_fields)\n        self.remove_invisible = remove_invisible\n        self.angle_in_degrees = angle_in_degrees\n        self.check_each_transform = check_each_transform\n\n    def to_dict_private(self) -> dict[str, Any]:\n        data = super().to_dict_private()\n        data.update(\n            {\n                \"remove_invisible\": self.remove_invisible,\n                \"angle_in_degrees\": self.angle_in_degrees,\n                \"check_each_transform\": self.check_each_transform,\n            },\n        )\n        return data\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return \"KeypointParams\"\n\n    def __repr__(self) -> str:\n        return (\n            f\"KeypointParams(format={self.format}, label_fields={self.label_fields},\"\n            f\" remove_invisible={self.remove_invisible}, angle_in_degrees={self.angle_in_degrees},\"\n            f\" check_each_transform={self.check_each_transform})\"\n        )\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.KeypointsProcessor","title":"class KeypointsProcessor (params, additional_targets=None) [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/keypoints_utils.py Python
class KeypointsProcessor(DataProcessor):\n    def __init__(self, params: KeypointParams, additional_targets: dict[str, str] | None = None):\n        super().__init__(params, additional_targets)\n\n    @property\n    def default_data_name(self) -> str:\n        return \"keypoints\"\n\n    def ensure_data_valid(self, data: dict[str, Any]) -> None:\n        if self.params.label_fields and not all(i in data for i in self.params.label_fields):\n            msg = \"Your 'label_fields' are not valid - them must have same names as params in 'keypoint_params' dict\"\n            raise ValueError(msg)\n\n    def filter(\n        self,\n        data: np.ndarray,\n        shape: ShapeType,\n    ) -> np.ndarray:\n        \"\"\"Filter keypoints based on visibility within given shape.\n\n        Args:\n            data: Keypoints in [x, y, z, angle, scale] format\n            shape: Shape to check against as {'height': height, 'width': width, 'depth': depth}\n\n        Returns:\n            Filtered keypoints\n        \"\"\"\n        self.params: KeypointParams\n        return filter_keypoints(data, shape, remove_invisible=self.params.remove_invisible)\n\n    def check(self, data: np.ndarray, shape: ShapeType) -> None:\n        check_keypoints(data, shape)\n\n    def convert_from_albumentations(\n        self,\n        data: np.ndarray,\n        shape: ShapeType,\n    ) -> np.ndarray:\n        if not data.size:\n            return data\n\n        params = self.params\n        return convert_keypoints_from_albumentations(\n            data,\n            params.format,\n            shape,\n            check_validity=params.remove_invisible,\n            angle_in_degrees=params.angle_in_degrees,\n        )\n\n    def convert_to_albumentations(\n        self,\n        data: np.ndarray,\n        shape: ShapeType,\n    ) -> np.ndarray:\n        if not data.size:\n            return data\n        params = self.params\n        return convert_keypoints_to_albumentations(\n            data,\n            params.format,\n            shape,\n            check_validity=params.remove_invisible,\n            angle_in_degrees=params.angle_in_degrees,\n        )\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.check_keypoints","title":"def check_keypoints (keypoints, shape) [view source on GitHub]","text":"

Check if keypoint coordinates are within valid ranges for the given shape.

This function validates that: 1. All x-coordinates are within [0, width) 2. All y-coordinates are within [0, height) 3. If depth is provided in shape, z-coordinates are within [0, depth) 4. Angles are within the range [0, 2\u03c0)

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 5+), where N is the number of keypoints. - First 2 columns are always x, y - Column 3 (if present) is z - Column 4 (if present) is angle - Column 5+ (if present) are additional attributes

shape ShapeType

The shape of the image/volume: - For 2D: {'height': int, 'width': int} - For 3D: {'height': int, 'width': int, 'depth': int}

Exceptions:

Type Description ValueError

If any keypoint coordinate is outside the valid range, or if angles are invalid. The error message will detail which keypoints are invalid and why.

Note

  • The function assumes that keypoint coordinates are in absolute pixel values, not normalized
  • Angles are in radians
  • Z-coordinates are only checked if 'depth' is present in shape
Source code in albumentations/core/keypoints_utils.py Python
def check_keypoints(keypoints: np.ndarray, shape: ShapeType) -> None:\n    \"\"\"Check if keypoint coordinates are within valid ranges for the given shape.\n\n    This function validates that:\n    1. All x-coordinates are within [0, width)\n    2. All y-coordinates are within [0, height)\n    3. If depth is provided in shape, z-coordinates are within [0, depth)\n    4. Angles are within the range [0, 2\u03c0)\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 5+), where N is the number of keypoints.\n            - First 2 columns are always x, y\n            - Column 3 (if present) is z\n            - Column 4 (if present) is angle\n            - Column 5+ (if present) are additional attributes\n        shape (ShapeType): The shape of the image/volume:\n                           - For 2D: {'height': int, 'width': int}\n                           - For 3D: {'height': int, 'width': int, 'depth': int}\n\n    Raises:\n        ValueError: If any keypoint coordinate is outside the valid range, or if angles are invalid.\n                   The error message will detail which keypoints are invalid and why.\n\n    Note:\n        - The function assumes that keypoint coordinates are in absolute pixel values, not normalized\n        - Angles are in radians\n        - Z-coordinates are only checked if 'depth' is present in shape\n    \"\"\"\n    height, width = shape[\"height\"], shape[\"width\"]\n    has_depth = \"depth\" in shape\n\n    # Check x and y coordinates (always present)\n    x, y = keypoints[:, 0], keypoints[:, 1]\n    invalid_x = np.where((x < 0) | (x >= width))[0]\n    invalid_y = np.where((y < 0) | (y >= height))[0]\n\n    error_messages = []\n\n    # Handle x, y errors\n    for idx in sorted(set(invalid_x) | set(invalid_y)):\n        if idx in invalid_x:\n            error_messages.append(\n                f\"Expected x for keypoint {keypoints[idx]} to be in range [0, {width}), got {x[idx]}\",\n            )\n        if idx in invalid_y:\n            error_messages.append(\n                f\"Expected y for keypoint {keypoints[idx]} to be in range [0, {height}), got {y[idx]}\",\n            )\n\n    # Check z coordinates if depth is provided and keypoints have z\n    if has_depth and keypoints.shape[1] > 2:\n        z = keypoints[:, 2]\n        depth = shape[\"depth\"]\n        invalid_z = np.where((z < 0) | (z >= depth))[0]\n        error_messages.extend(\n            f\"Expected z for keypoint {keypoints[idx]} to be in range [0, {depth}), got {z[idx]}\" for idx in invalid_z\n        )\n\n    # Check angles only if keypoints have angle column\n    if keypoints.shape[1] > 3:\n        angles = keypoints[:, 3]\n        invalid_angles = np.where((angles < 0) | (angles >= 2 * math.pi))[0]\n        error_messages.extend(\n            f\"Expected angle for keypoint {keypoints[idx]} to be in range [0, 2\u03c0), got {angles[idx]}\"\n            for idx in invalid_angles\n        )\n\n    if error_messages:\n        raise ValueError(\"\\n\".join(error_messages))\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.convert_keypoints_from_albumentations","title":"def convert_keypoints_from_albumentations (keypoints, target_format, shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]","text":"

Convert keypoints from Albumentations format to various other formats.

This function takes keypoints in the standard Albumentations format [x, y, z, angle, scale] and converts them to the specified target format.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints in Albumentations format with shape (N, 5+), where N is the number of keypoints. Each row represents a keypoint [x, y, z, angle, scale, ...].

target_format Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"]

The desired output format. - \"xy\": [x, y] - \"yx\": [y, x] - \"xya\": [x, y, angle] - \"xys\": [x, y, scale] - \"xyas\": [x, y, angle, scale] - \"xysa\": [x, y, scale, angle] - \"xyz\": [x, y, z]

shape ShapeType

The shape of the image {'height': height, 'width': width, 'depth': depth}.

check_validity bool

If True, check if the keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert output angles to degrees. If False, angles remain in radians. Defaults to True.

Returns:

Type Description np.ndarray

Array of keypoints in the specified target format with shape (N, 2+). Any additional columns from the input keypoints beyond the first 5 are preserved and appended after the converted columns.

Exceptions:

Type Description ValueError

If the target_format is not one of the supported formats.

Note

  • Input angles are assumed to be in the range [0, 2\u03c0) radians
  • If the input keypoints have additional columns beyond the first 5, these columns are preserved in the output
Source code in albumentations/core/keypoints_utils.py Python
def convert_keypoints_from_albumentations(\n    keypoints: np.ndarray,\n    target_format: Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"],\n    shape: ShapeType,\n    check_validity: bool = False,\n    angle_in_degrees: bool = True,\n) -> np.ndarray:\n    \"\"\"Convert keypoints from Albumentations format to various other formats.\n\n    This function takes keypoints in the standard Albumentations format [x, y, z, angle, scale]\n    and converts them to the specified target format.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints in Albumentations format with shape (N, 5+),\n                                where N is the number of keypoints. Each row represents a keypoint\n                                [x, y, z, angle, scale, ...].\n        target_format (Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"]): The desired output format.\n            - \"xy\": [x, y]\n            - \"yx\": [y, x]\n            - \"xya\": [x, y, angle]\n            - \"xys\": [x, y, scale]\n            - \"xyas\": [x, y, angle, scale]\n            - \"xysa\": [x, y, scale, angle]\n            - \"xyz\": [x, y, z]\n        shape (ShapeType): The shape of the image {'height': height, 'width': width, 'depth': depth}.\n        check_validity (bool, optional): If True, check if the keypoints are within the image boundaries.\n                                         Defaults to False.\n        angle_in_degrees (bool, optional): If True, convert output angles to degrees.\n                                           If False, angles remain in radians.\n                                           Defaults to True.\n\n    Returns:\n        np.ndarray: Array of keypoints in the specified target format with shape (N, 2+).\n                    Any additional columns from the input keypoints beyond the first 5\n                    are preserved and appended after the converted columns.\n\n    Raises:\n        ValueError: If the target_format is not one of the supported formats.\n\n    Note:\n        - Input angles are assumed to be in the range [0, 2\u03c0) radians\n        - If the input keypoints have additional columns beyond the first 5,\n          these columns are preserved in the output\n    \"\"\"\n    if target_format not in keypoint_formats:\n        raise ValueError(f\"Unknown target_format {target_format}. Supported formats are: {keypoint_formats}\")\n\n    x, y, z, angle, scale = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2], keypoints[:, 3], keypoints[:, 4]\n    angle = angle_to_2pi_range(angle)\n\n    if check_validity:\n        check_keypoints(np.column_stack((x, y, z, angle, scale)), shape)\n\n    if angle_in_degrees:\n        angle = np.degrees(angle)\n\n    format_to_columns = {\n        \"xy\": [x, y],\n        \"yx\": [y, x],\n        \"xya\": [x, y, angle],\n        \"xys\": [x, y, scale],\n        \"xyas\": [x, y, angle, scale],\n        \"xysa\": [x, y, scale, angle],\n        \"xyz\": [x, y, z],\n    }\n\n    result = np.column_stack(format_to_columns[target_format])\n\n    # Add any additional columns from the original keypoints\n    if keypoints.shape[1] > NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:\n        return np.column_stack((result, keypoints[:, NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS:]))\n\n    return result\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.convert_keypoints_to_albumentations","title":"def convert_keypoints_to_albumentations (keypoints, source_format, shape, check_validity=False, angle_in_degrees=True) [view source on GitHub]","text":"

Convert keypoints from various formats to the Albumentations format.

This function takes keypoints in different formats and converts them to the standard Albumentations format: [x, y, z, angle, scale]. For 2D formats, z is set to 0. For formats without angle or scale, these values are set to 0.

Parameters:

Name Type Description keypoints np.ndarray

Array of keypoints with shape (N, 2+), where N is the number of keypoints. The number of columns depends on the source_format.

source_format Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"]

The format of the input keypoints. - \"xy\": [x, y] - \"yx\": [y, x] - \"xya\": [x, y, angle] - \"xys\": [x, y, scale] - \"xyas\": [x, y, angle, scale] - \"xysa\": [x, y, scale, angle] - \"xyz\": [x, y, z]

shape ShapeType

The shape of the image {'height': height, 'width': width, 'depth': depth}.

check_validity bool

If True, check if the converted keypoints are within the image boundaries. Defaults to False.

angle_in_degrees bool

If True, convert input angles from degrees to radians. Defaults to True.

Returns:

Type Description np.ndarray

Array of keypoints in Albumentations format [x, y, z, angle, scale] with shape (N, 5+). Any additional columns from the input keypoints are preserved and appended after the first 5 columns.

Exceptions:

Type Description ValueError

If the source_format is not one of the supported formats.

Note

  • For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0
  • Angles are converted to the range [0, 2\u03c0) radians
  • If the input keypoints have additional columns beyond what's specified in the source_format, these columns are preserved in the output
Source code in albumentations/core/keypoints_utils.py Python
def convert_keypoints_to_albumentations(\n    keypoints: np.ndarray,\n    source_format: Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"],\n    shape: ShapeType,\n    check_validity: bool = False,\n    angle_in_degrees: bool = True,\n) -> np.ndarray:\n    \"\"\"Convert keypoints from various formats to the Albumentations format.\n\n    This function takes keypoints in different formats and converts them to the standard\n    Albumentations format: [x, y, z, angle, scale]. For 2D formats, z is set to 0.\n    For formats without angle or scale, these values are set to 0.\n\n    Args:\n        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.\n                                The number of columns depends on the source_format.\n        source_format (Literal[\"xy\", \"yx\", \"xya\", \"xys\", \"xyas\", \"xysa\", \"xyz\"]): The format of the input keypoints.\n            - \"xy\": [x, y]\n            - \"yx\": [y, x]\n            - \"xya\": [x, y, angle]\n            - \"xys\": [x, y, scale]\n            - \"xyas\": [x, y, angle, scale]\n            - \"xysa\": [x, y, scale, angle]\n            - \"xyz\": [x, y, z]\n        shape (ShapeType): The shape of the image {'height': height, 'width': width, 'depth': depth}.\n        check_validity (bool, optional): If True, check if the converted keypoints are within the image boundaries.\n                                         Defaults to False.\n        angle_in_degrees (bool, optional): If True, convert input angles from degrees to radians.\n                                           Defaults to True.\n\n    Returns:\n        np.ndarray: Array of keypoints in Albumentations format [x, y, z, angle, scale] with shape (N, 5+).\n                    Any additional columns from the input keypoints are preserved and appended after the\n                    first 5 columns.\n\n    Raises:\n        ValueError: If the source_format is not one of the supported formats.\n\n    Note:\n        - For 2D formats (xy, yx, xya, xys, xyas, xysa), z coordinate is set to 0\n        - Angles are converted to the range [0, 2\u03c0) radians\n        - If the input keypoints have additional columns beyond what's specified in the source_format,\n          these columns are preserved in the output\n    \"\"\"\n    if source_format not in keypoint_formats:\n        raise ValueError(f\"Unknown source_format {source_format}. Supported formats are: {keypoint_formats}\")\n\n    format_to_indices: dict[str, list[int | None]] = {\n        \"xy\": [0, 1, None, None, None],\n        \"yx\": [1, 0, None, None, None],\n        \"xya\": [0, 1, None, 2, None],\n        \"xys\": [0, 1, None, None, 2],\n        \"xyas\": [0, 1, None, 2, 3],\n        \"xysa\": [0, 1, None, 3, 2],\n        \"xyz\": [0, 1, 2, None, None],\n    }\n\n    indices: list[int | None] = format_to_indices[source_format]\n\n    processed_keypoints = np.zeros((keypoints.shape[0], NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS), dtype=np.float32)\n\n    for i, idx in enumerate(indices):\n        if idx is not None:\n            processed_keypoints[:, i] = keypoints[:, idx]\n\n    if angle_in_degrees and indices[3] is not None:  # angle is now at index 3\n        processed_keypoints[:, 3] = np.radians(processed_keypoints[:, 3])\n\n    processed_keypoints[:, 3] = angle_to_2pi_range(processed_keypoints[:, 3])  # angle is now at index 3\n\n    if keypoints.shape[1] > len(source_format):\n        processed_keypoints = np.column_stack((processed_keypoints, keypoints[:, len(source_format) :]))\n\n    if check_validity:\n        check_keypoints(processed_keypoints, shape)\n\n    return processed_keypoints\n
"},{"location":"api_reference/core/keypoints_utils/#albumentations.core.keypoints_utils.filter_keypoints","title":"def filter_keypoints (keypoints, shape, remove_invisible) [view source on GitHub]","text":"

Filter keypoints to remove those outside the boundaries.

Parameters:

Name Type Description keypoints np.ndarray

A numpy array of shape (N, 5+) where N is the number of keypoints. Each row represents a keypoint (x, y, z, angle, scale, ...).

shape ShapeType

Shape to check against as {'height': height, 'width': width, 'depth': depth}.

remove_invisible bool

If True, remove keypoints outside the boundaries.

Returns:

Type Description np.ndarray

A numpy array of filtered keypoints.

Source code in albumentations/core/keypoints_utils.py Python
def filter_keypoints(\n    keypoints: np.ndarray,\n    shape: ShapeType,\n    remove_invisible: bool,\n) -> np.ndarray:\n    \"\"\"Filter keypoints to remove those outside the boundaries.\n\n    Args:\n        keypoints: A numpy array of shape (N, 5+) where N is the number of keypoints.\n                   Each row represents a keypoint (x, y, z, angle, scale, ...).\n        shape: Shape to check against as {'height': height, 'width': width, 'depth': depth}.\n        remove_invisible: If True, remove keypoints outside the boundaries.\n\n    Returns:\n        A numpy array of filtered keypoints.\n    \"\"\"\n    if not remove_invisible:\n        return keypoints\n\n    if not keypoints.size:\n        return keypoints\n\n    height, width, depth = shape[\"height\"], shape[\"width\"], shape.get(\"depth\", None)\n\n    # Create boolean mask for visible keypoints\n    x, y, z = keypoints[:, 0], keypoints[:, 1], keypoints[:, 2]\n    visible = (x >= 0) & (x < width) & (y >= 0) & (y < height)\n\n    if depth is not None:\n        visible &= (z >= 0) & (z < depth)\n\n    # Apply the mask to filter keypoints\n    return keypoints[visible]\n
"},{"location":"api_reference/core/serialization/","title":"Serialization API (core.serialization)","text":""},{"location":"api_reference/core/serialization/#albumentations.core.serialization.Serializable","title":"class Serializable [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/serialization.py Python
class Serializable(metaclass=SerializableMeta):\n    @classmethod\n    @abstractmethod\n    def is_serializable(cls) -> bool:\n        raise NotImplementedError\n\n    @classmethod\n    @abstractmethod\n    def get_class_fullname(cls) -> str:\n        raise NotImplementedError\n\n    @abstractmethod\n    def to_dict_private(self) -> dict[str, Any]:\n        raise NotImplementedError\n\n    def to_dict(self, on_not_implemented_error: str = \"raise\") -> dict[str, Any]:\n        \"\"\"Take a transform pipeline and convert it to a serializable representation that uses only standard\n        python data types: dictionaries, lists, strings, integers, and floats.\n\n        Args:\n            self: A transform that should be serialized. If the transform doesn't implement the `to_dict`\n                method and `on_not_implemented_error` equals to 'raise' then `NotImplementedError` is raised.\n                If `on_not_implemented_error` equals to 'warn' then `NotImplementedError` will be ignored\n                but no transform parameters will be serialized.\n            on_not_implemented_error (str): `raise` or `warn`.\n\n        \"\"\"\n        if on_not_implemented_error not in {\"raise\", \"warn\"}:\n            msg = f\"Unknown on_not_implemented_error value: {on_not_implemented_error}. Supported values are: 'raise' \"\n            \"and 'warn'\"\n            raise ValueError(msg)\n        try:\n            transform_dict = self.to_dict_private()\n        except NotImplementedError:\n            if on_not_implemented_error == \"raise\":\n                raise\n\n            transform_dict = {}\n            warnings.warn(\n                f\"Got NotImplementedError while trying to serialize {self}. Object arguments are not preserved. \"\n                f\"Implement either '{self.__class__.__name__}.get_transform_init_args_names' \"\n                f\"or '{self.__class__.__name__}.get_transform_init_args' \"\n                \"method to make the transform serializable\",\n                stacklevel=2,\n            )\n        return {\"__version__\": __version__, \"transform\": transform_dict}\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.SerializableMeta","title":"class SerializableMeta [view source on GitHub]","text":"

A metaclass that is used to register classes in SERIALIZABLE_REGISTRY or NON_SERIALIZABLE_REGISTRY so they can be found later while deserializing transformation pipeline using classes full names.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/serialization.py Python
class SerializableMeta(ABCMeta):\n    \"\"\"A metaclass that is used to register classes in `SERIALIZABLE_REGISTRY` or `NON_SERIALIZABLE_REGISTRY`\n    so they can be found later while deserializing transformation pipeline using classes full names.\n    \"\"\"\n\n    def __new__(cls, name: str, bases: tuple[type, ...], *args: Any, **kwargs: Any) -> SerializableMeta:\n        cls_obj = super().__new__(cls, name, bases, *args, **kwargs)\n        if name != \"Serializable\" and ABC not in bases:\n            if cls_obj.is_serializable():\n                SERIALIZABLE_REGISTRY[cls_obj.get_class_fullname()] = cls_obj\n            else:\n                NON_SERIALIZABLE_REGISTRY[cls_obj.get_class_fullname()] = cls_obj\n        return cls_obj\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return False\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def _to_dict(cls) -> dict[str, Any]:\n        return {}\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.from_dict","title":"def from_dict (transform_dict, nonserializable=None) [view source on GitHub]","text":"

transform_dict: A dictionary with serialized transform pipeline. nonserializable (dict): A dictionary that contains non-serializable transforms. This dictionary is required when you are restoring a pipeline that contains non-serializable transforms. Keys in that dictionary should be named same as name arguments in respective transforms from a serialized pipeline.

Source code in albumentations/core/serialization.py Python
def from_dict(\n    transform_dict: dict[str, Any],\n    nonserializable: dict[str, Any] | None = None,\n) -> Serializable | None:\n    \"\"\"Args:\n    transform_dict: A dictionary with serialized transform pipeline.\n    nonserializable (dict): A dictionary that contains non-serializable transforms.\n        This dictionary is required when you are restoring a pipeline that contains non-serializable transforms.\n        Keys in that dictionary should be named same as `name` arguments in respective transforms from\n        a serialized pipeline.\n\n    \"\"\"\n    register_additional_transforms()\n    transform = transform_dict[\"transform\"]\n    lmbd = instantiate_nonserializable(transform, nonserializable)\n    if lmbd:\n        return lmbd\n    name = transform[\"__class_fullname__\"]\n    args = {k: v for k, v in transform.items() if k != \"__class_fullname__\"}\n    cls = SERIALIZABLE_REGISTRY[shorten_class_name(name)]\n    if \"transforms\" in args:\n        args[\"transforms\"] = [from_dict({\"transform\": t}, nonserializable=nonserializable) for t in args[\"transforms\"]]\n    return cls(**args)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.get_shortest_class_fullname","title":"def get_shortest_class_fullname (cls) [view source on GitHub]","text":"

The function get_shortest_class_fullname takes a class object as input and returns its shortened full name.

:param cls: The parameter cls is of type Type[BasicCompose], which means it expects a class that is a subclass of BasicCompose :type cls: Type[BasicCompose] :return: a string, which is the shortened version of the full class name.

Source code in albumentations/core/serialization.py Python
def get_shortest_class_fullname(cls: type[Any]) -> str:\n    \"\"\"The function `get_shortest_class_fullname` takes a class object as input and returns its shortened\n    full name.\n\n    :param cls: The parameter `cls` is of type `Type[BasicCompose]`, which means it expects a class that\n    is a subclass of `BasicCompose`\n    :type cls: Type[BasicCompose]\n    :return: a string, which is the shortened version of the full class name.\n    \"\"\"\n    class_fullname = f\"{cls.__module__}.{cls.__name__}\"\n    return shorten_class_name(class_fullname)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.load","title":"def load (filepath_or_buffer, data_format='json', nonserializable=None) [view source on GitHub]","text":"

Load a serialized pipeline from a file or file-like object and construct a transform pipeline.

Parameters:

Name Type Description filepath_or_buffer Union[str, Path, TextIO]

The file path or file-like object to read the serialized data from. If a string is provided, it is interpreted as a path to a file. If a file-like object is provided, the serialized data will be read from it directly.

data_format str

The format of the serialized data. Valid options are 'json' and 'yaml'. Defaults to 'json'.

nonserializable Optional[dict[str, Any]]

A dictionary that contains non-serializable transforms. This dictionary is required when restoring a pipeline that contains non-serializable transforms. Keys in the dictionary should be named the same as the name arguments in respective transforms from the serialized pipeline. Defaults to None.

Returns:

Type Description object

The deserialized transform pipeline.

Exceptions:

Type Description ValueError

If data_format is 'yaml' but PyYAML is not installed.

Source code in albumentations/core/serialization.py Python
def load(\n    filepath_or_buffer: str | Path | TextIO,\n    data_format: str = \"json\",\n    nonserializable: dict[str, Any] | None = None,\n) -> object:\n    \"\"\"Load a serialized pipeline from a file or file-like object and construct a transform pipeline.\n\n    Args:\n        filepath_or_buffer (Union[str, Path, TextIO]): The file path or file-like object to read the serialized\n            data from.\n            If a string is provided, it is interpreted as a path to a file. If a file-like object is provided,\n            the serialized data will be read from it directly.\n        data_format (str): The format of the serialized data. Valid options are 'json' and 'yaml'.\n            Defaults to 'json'.\n        nonserializable (Optional[dict[str, Any]]): A dictionary that contains non-serializable transforms.\n            This dictionary is required when restoring a pipeline that contains non-serializable transforms.\n            Keys in the dictionary should be named the same as the `name` arguments in respective transforms\n            from the serialized pipeline. Defaults to None.\n\n    Returns:\n        object: The deserialized transform pipeline.\n\n    Raises:\n        ValueError: If `data_format` is 'yaml' but PyYAML is not installed.\n\n    \"\"\"\n    check_data_format(data_format)\n\n    if isinstance(filepath_or_buffer, (str, Path)):  # Assume it's a filepath\n        with open(filepath_or_buffer) as f:\n            if data_format == \"json\":\n                transform_dict = json.load(f)\n            else:\n                if not yaml_available:\n                    msg = \"You need to install PyYAML to load a pipeline in yaml format\"\n                    raise ValueError(msg)\n                transform_dict = yaml.safe_load(f)\n    elif data_format == \"json\":\n        transform_dict = json.load(filepath_or_buffer)\n    else:\n        if not yaml_available:\n            msg = \"You need to install PyYAML to load a pipeline in yaml format\"\n            raise ValueError(msg)\n        transform_dict = yaml.safe_load(filepath_or_buffer)\n\n    return from_dict(transform_dict, nonserializable=nonserializable)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.register_additional_transforms","title":"def register_additional_transforms () [view source on GitHub]","text":"

Register transforms that are not imported directly into the albumentations module by checking the availability of optional dependencies.

Source code in albumentations/core/serialization.py Python
def register_additional_transforms() -> None:\n    \"\"\"Register transforms that are not imported directly into the `albumentations` module by checking\n    the availability of optional dependencies.\n    \"\"\"\n    if importlib.util.find_spec(\"torch\") is not None:\n        try:\n            # Import `albumentations.pytorch` only if `torch` is installed.\n            import albumentations.pytorch\n\n            # Use a dummy operation to acknowledge the use of the imported module and avoid linting errors.\n            _ = albumentations.pytorch.ToTensorV2\n        except ImportError:\n            pass\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.save","title":"def save (transform, filepath_or_buffer, data_format='json', on_not_implemented_error='raise') [view source on GitHub]","text":"

Serialize a transform pipeline and save it to either a file specified by a path or a file-like object in either JSON or YAML format.

Parameters:

Name Type Description transform Serializable

The transform pipeline to serialize.

filepath_or_buffer Union[str, Path, TextIO]

The file path or file-like object to write the serialized data to. If a string is provided, it is interpreted as a path to a file. If a file-like object is provided, the serialized data will be written to it directly.

data_format str

The format to serialize the data in. Valid options are 'json' and 'yaml'. Defaults to 'json'.

on_not_implemented_error str

Determines the behavior if a transform does not implement the to_dict method. If set to 'raise', a NotImplementedError is raised. If set to 'warn', the exception is ignored, and no transform arguments are saved. Defaults to 'raise'.

Exceptions:

Type Description ValueError

If data_format is 'yaml' but PyYAML is not installed.

Source code in albumentations/core/serialization.py Python
def save(\n    transform: Serializable,\n    filepath_or_buffer: str | Path | TextIO,\n    data_format: str = \"json\",\n    on_not_implemented_error: str = \"raise\",\n) -> None:\n    \"\"\"Serialize a transform pipeline and save it to either a file specified by a path or a file-like object\n    in either JSON or YAML format.\n\n    Args:\n        transform (Serializable): The transform pipeline to serialize.\n        filepath_or_buffer (Union[str, Path, TextIO]): The file path or file-like object to write the serialized\n            data to.\n            If a string is provided, it is interpreted as a path to a file. If a file-like object is provided,\n            the serialized data will be written to it directly.\n        data_format (str): The format to serialize the data in. Valid options are 'json' and 'yaml'.\n            Defaults to 'json'.\n        on_not_implemented_error (str): Determines the behavior if a transform does not implement the `to_dict` method.\n            If set to 'raise', a `NotImplementedError` is raised. If set to 'warn', the exception is ignored, and\n            no transform arguments are saved. Defaults to 'raise'.\n\n    Raises:\n        ValueError: If `data_format` is 'yaml' but PyYAML is not installed.\n\n    \"\"\"\n    check_data_format(data_format)\n    transform_dict = transform.to_dict(on_not_implemented_error=on_not_implemented_error)\n    transform_dict = serialize_enum(transform_dict)\n\n    # Determine whether to write to a file or a file-like object\n    if isinstance(filepath_or_buffer, (str, Path)):  # It's a filepath\n        with open(filepath_or_buffer, \"w\") as f:\n            if data_format == \"yaml\":\n                if not yaml_available:\n                    msg = \"You need to install PyYAML to save a pipeline in YAML format\"\n                    raise ValueError(msg)\n                yaml.safe_dump(transform_dict, f, default_flow_style=False)\n            elif data_format == \"json\":\n                json.dump(transform_dict, f)\n    elif data_format == \"yaml\":\n        if not yaml_available:\n            msg = \"You need to install PyYAML to save a pipeline in YAML format\"\n            raise ValueError(msg)\n        yaml.safe_dump(transform_dict, filepath_or_buffer, default_flow_style=False)\n    elif data_format == \"json\":\n        json.dump(transform_dict, filepath_or_buffer, indent=2)\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.serialize_enum","title":"def serialize_enum (obj) [view source on GitHub]","text":"

Recursively search for Enum objects and convert them to their value. Also handle any Mapping or Sequence types.

Source code in albumentations/core/serialization.py Python
def serialize_enum(obj: Any) -> Any:\n    \"\"\"Recursively search for Enum objects and convert them to their value.\n    Also handle any Mapping or Sequence types.\n    \"\"\"\n    if isinstance(obj, Mapping):\n        return {k: serialize_enum(v) for k, v in obj.items()}\n    if isinstance(obj, Sequence) and not isinstance(obj, str):  # exclude strings since they're also sequences\n        return [serialize_enum(v) for v in obj]\n    return obj.value if isinstance(obj, Enum) else obj\n
"},{"location":"api_reference/core/serialization/#albumentations.core.serialization.to_dict","title":"def to_dict (transform, on_not_implemented_error='raise') [view source on GitHub]","text":"

Take a transform pipeline and convert it to a serializable representation that uses only standard python data types: dictionaries, lists, strings, integers, and floats.

Parameters:

Name Type Description transform Serializable

A transform that should be serialized. If the transform doesn't implement the to_dict method and on_not_implemented_error equals to 'raise' then NotImplementedError is raised. If on_not_implemented_error equals to 'warn' then NotImplementedError will be ignored but no transform parameters will be serialized.

on_not_implemented_error str

raise or warn.

Source code in albumentations/core/serialization.py Python
def to_dict(transform: Serializable, on_not_implemented_error: str = \"raise\") -> dict[str, Any]:\n    \"\"\"Take a transform pipeline and convert it to a serializable representation that uses only standard\n    python data types: dictionaries, lists, strings, integers, and floats.\n\n    Args:\n        transform: A transform that should be serialized. If the transform doesn't implement the `to_dict`\n            method and `on_not_implemented_error` equals to 'raise' then `NotImplementedError` is raised.\n            If `on_not_implemented_error` equals to 'warn' then `NotImplementedError` will be ignored\n            but no transform parameters will be serialized.\n        on_not_implemented_error (str): `raise` or `warn`.\n\n    \"\"\"\n    return transform.to_dict(on_not_implemented_error)\n
"},{"location":"api_reference/core/transforms_interface/","title":"Transforms Interface (core.transforms_interface)","text":""},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.BaseTransformInitSchema","title":"class BaseTransformInitSchema ","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class BaseTransformInitSchema(BaseModel):\n    model_config = ConfigDict(arbitrary_types_allowed=True)\n    always_apply: bool | None\n    p: ProbabilityType\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.BasicTransform","title":"class BasicTransform (p=0.5, always_apply=None) [view source on GitHub]","text":"

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class BasicTransform(Serializable, metaclass=CombinedMeta):\n    _targets: tuple[Targets, ...] | Targets  # targets that this transform can work on\n    _available_keys: set[str]  # targets that this transform, as string, lower-cased\n    _key2func: dict[\n        str,\n        Callable[..., Any],\n    ]  # mapping for targets (plus additional targets) and methods for which they depend\n    call_backup = None\n    interpolation: int\n    fill: DropoutFillValue\n    fill_mask: ColorType | None\n    # replay mode params\n    deterministic: bool = False\n    save_key = \"replay\"\n    replay_mode = False\n    applied_in_replay = False\n\n    class InitSchema(BaseTransformInitSchema):\n        pass\n\n    def __init__(self, p: float = 0.5, always_apply: bool | None = None):\n        self.p = p\n        if always_apply is not None:\n            if always_apply:\n                warn(\n                    \"always_apply is deprecated. Use `p=1` if you want to always apply the transform.\"\n                    \" self.p will be set to 1.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n                self.p = 1.0\n            else:\n                warn(\n                    \"always_apply is deprecated.\",\n                    DeprecationWarning,\n                    stacklevel=2,\n                )\n        self._additional_targets: dict[str, str] = {}\n        # replay mode params\n        self.params: dict[Any, Any] = {}\n        self._key2func = {}\n        self._set_keys()\n        self.processors: dict[str, BboxProcessor | KeypointsProcessor] = {}\n        self.seed: int | None = None\n        self.random_generator = np.random.default_rng(self.seed)\n        self.py_random = random.Random(self.seed)\n\n    def set_random_state(\n        self,\n        random_generator: np.random.Generator,\n        py_random: random.Random,\n    ) -> None:\n        \"\"\"Set random state directly from generators.\n\n        Args:\n            random_generator: numpy random generator to use\n            py_random: python random generator to use\n        \"\"\"\n        self.random_generator = random_generator\n        self.py_random = py_random\n\n    def set_random_seed(self, seed: int | None) -> None:\n        \"\"\"Set random state from seed.\n\n        Args:\n            seed: Random seed to use\n        \"\"\"\n        self.seed = seed\n        self.random_generator = np.random.default_rng(seed)\n        self.py_random = random.Random(seed)\n\n    def get_dict_with_id(self) -> dict[str, Any]:\n        d = self.to_dict_private()\n        d[\"id\"] = id(self)\n        return d\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        \"\"\"Returns names of arguments that are used in __init__ method of the transform.\"\"\"\n        msg = (\n            f\"Class {self.get_class_fullname()} is not serializable because the `get_transform_init_args_names` \"\n            \"method is not implemented\"\n        )\n        raise NotImplementedError(msg)\n\n    def set_processors(self, processors: dict[str, BboxProcessor | KeypointsProcessor]) -> None:\n        self.processors = processors\n\n    def get_processor(self, key: str) -> BboxProcessor | KeypointsProcessor | None:\n        return self.processors.get(key)\n\n    def __call__(self, *args: Any, force_apply: bool = False, **kwargs: Any) -> Any:\n        if args:\n            msg = \"You have to pass data to augmentations as named arguments, for example: aug(image=image)\"\n            raise KeyError(msg)\n        if self.replay_mode:\n            if self.applied_in_replay:\n                return self.apply_with_params(self.params, **kwargs)\n            return kwargs\n\n        # Reset params at the start of each call\n        self.params = {}\n\n        if self.should_apply(force_apply=force_apply):\n            params = self.get_params()\n            params = self.update_params_shape(params=params, data=kwargs)\n\n            if self.targets_as_params:  # check if all required targets are in kwargs.\n                missing_keys = set(self.targets_as_params).difference(kwargs.keys())\n                if missing_keys and not (missing_keys == {\"image\"} and \"images\" in kwargs):\n                    msg = f\"{self.__class__.__name__} requires {self.targets_as_params} missing keys: {missing_keys}\"\n                    raise ValueError(msg)\n\n            params_dependent_on_data = self.get_params_dependent_on_data(params=params, data=kwargs)\n            params.update(params_dependent_on_data)\n\n            if self.targets_as_params:  # this block will be removed after removing `get_params_dependent_on_targets`\n                targets_as_params = {k: kwargs.get(k) for k in self.targets_as_params}\n                if missing_keys:  # here we expecting case when missing_keys == {\"image\"} and \"images\" in kwargs\n                    targets_as_params[\"image\"] = kwargs[\"images\"][0]\n                params_dependent_on_targets = self.get_params_dependent_on_targets(targets_as_params)\n                params.update(params_dependent_on_targets)\n\n            # Store the final params\n            self.params = params\n\n            if self.deterministic:\n                kwargs[self.save_key][id(self)] = deepcopy(params)\n            return self.apply_with_params(params, **kwargs)\n\n        return kwargs\n\n    def get_applied_params(self) -> dict[str, Any]:\n        \"\"\"Returns the parameters that were used in the last transform application.\n        Returns empty dict if transform was not applied.\n        \"\"\"\n        return self.params\n\n    def should_apply(self, force_apply: bool = False) -> bool:\n        if self.p <= 0.0:\n            return False\n        if self.p >= 1.0 or force_apply:\n            return True\n        return self.py_random.random() < self.p\n\n    def apply_with_params(self, params: dict[str, Any], *args: Any, **kwargs: Any) -> dict[str, Any]:\n        \"\"\"Apply transforms with parameters.\"\"\"\n        params = self.update_params(params, **kwargs)  # remove after move parameters like interpolation\n        res = {}\n        for key, arg in kwargs.items():\n            if key in self._key2func and arg is not None:\n                target_function = self._key2func[key]\n                res[key] = ensure_contiguous_output(\n                    target_function(ensure_contiguous_output(arg), **params),\n                )\n            else:\n                res[key] = arg\n        return res\n\n    def set_deterministic(self, flag: bool, save_key: str = \"replay\") -> BasicTransform:\n        \"\"\"Set transform to be deterministic.\"\"\"\n        if save_key == \"params\":\n            msg = \"params save_key is reserved\"\n            raise KeyError(msg)\n\n        self.deterministic = flag\n        if self.deterministic and self.targets_as_params:\n            warn(\n                self.get_class_fullname() + \" could work incorrectly in ReplayMode for other input data\"\n                \" because its' params depend on targets.\",\n                stacklevel=2,\n            )\n        self.save_key = save_key\n        return self\n\n    def __repr__(self) -> str:\n        state = self.get_base_init_args()\n        state.update(self.get_transform_init_args())\n        return f\"{self.__class__.__name__}({format_args(state)})\"\n\n    def apply(self, img: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform on image.\"\"\"\n        raise NotImplementedError\n\n    def apply_to_images(self, images: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform on images.\n\n        Args:\n            images: Input images as numpy array of shape:\n                - (num_images, height, width, channels)\n                - (num_images, height, width) for grayscale\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed images as numpy array in the same format as input\n        \"\"\"\n        # Handle batched numpy array input\n        transformed = np.stack([self.apply(image, **params) for image in images])\n        return np.require(transformed, requirements=[\"C_CONTIGUOUS\"])\n\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform slice by slice to a volume.\n\n        Args:\n            volume: Input volume of shape (depth, height, width) or (depth, height, width, channels)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed volume as numpy array in the same format as input\n        \"\"\"\n        return self.apply_to_images(volume, *args, **params)\n\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to multiple volumes.\"\"\"\n        return np.stack([self.apply_to_volume(vol, *args, **params) for vol in volumes])\n\n    def get_params(self) -> dict[str, Any]:\n        \"\"\"Returns parameters independent of input.\"\"\"\n        return {}\n\n    def update_params_shape(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Updates parameters with input shape.\"\"\"\n        # Extract shape from volume, volumes, image, or images\n        if \"volume\" in data:\n            shape = data[\"volume\"][0].shape  # Take first slice of volume\n        elif \"volumes\" in data:\n            shape = data[\"volumes\"][0][0].shape  # Take first slice of first volume\n        elif \"image\" in data:\n            shape = data[\"image\"].shape\n        else:\n            shape = data[\"images\"][0].shape\n\n        # For volumes/images, shape will be either (H, W) or (H, W, C)\n        params[\"shape\"] = shape\n        return params\n\n    def get_params_dependent_on_data(self, params: dict[str, Any], data: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"Returns parameters dependent on input.\"\"\"\n        return params\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        # mapping for targets and methods for which they depend\n        # for example:\n        # >>  {\"image\": self.apply}\n        # >>  {\"masks\": self.apply_to_masks}\n        raise NotImplementedError\n\n    def _set_keys(self) -> None:\n        \"\"\"Set _available_keys.\"\"\"\n        if not hasattr(self, \"_targets\"):\n            self._available_keys = set()\n        else:\n            self._available_keys = {\n                target.value.lower()\n                for target in (self._targets if isinstance(self._targets, tuple) else [self._targets])\n            }\n        self._available_keys.update(self.targets.keys())\n        self._key2func = {key: self.targets[key] for key in self._available_keys if key in self.targets}\n\n    @property\n    def available_keys(self) -> set[str]:\n        \"\"\"Returns set of available keys.\"\"\"\n        return self._available_keys\n\n    def update_params(self, params: dict[str, Any], **kwargs: Any) -> dict[str, Any]:\n        \"\"\"Update parameters with transform specific params.\n        This method is deprecated, use:\n        - `get_params` for transform specific params like interpolation and\n        - `update_params_shape` for data like shape.\n        \"\"\"\n        if hasattr(self, \"interpolation\"):\n            params[\"interpolation\"] = self.interpolation\n        if hasattr(self, \"fill\"):\n            params[\"fill\"] = self.fill\n        if hasattr(self, \"fill_mask\"):\n            params[\"fill_mask\"] = self.fill_mask\n\n        # Use update_params_shape to get shape consistently\n        return self.update_params_shape(params, kwargs)\n\n    def add_targets(self, additional_targets: dict[str, str]) -> None:\n        \"\"\"Add targets to transform them the same way as one of existing targets.\n        ex: {'target_image': 'image'}\n        ex: {'obj1_mask': 'mask', 'obj2_mask': 'mask'}\n        by the way you must have at least one object with key 'image'\n\n        Args:\n            additional_targets (dict): keys - new target name, values - old target name. ex: {'image2': 'image'}\n\n        \"\"\"\n        for k, v in additional_targets.items():\n            if k in self._additional_targets and v != self._additional_targets[k]:\n                raise ValueError(\n                    f\"Trying to overwrite existed additional targets. \"\n                    f\"Key={k} Exists={self._additional_targets[k]} New value: {v}\",\n                )\n            if v in self._available_keys:\n                self._additional_targets[k] = v\n                self._key2func[k] = self.targets[v]\n                self._available_keys.add(k)\n\n    @property\n    def targets_as_params(self) -> list[str]:\n        \"\"\"Targets used to get params dependent on targets.\n        This is used to check input has all required targets.\n        \"\"\"\n        return []\n\n    def get_params_dependent_on_targets(self, params: dict[str, Any]) -> dict[str, Any]:\n        \"\"\"This method is deprecated.\n        Use `get_params_dependent_on_data` instead.\n        Returns parameters dependent on targets.\n        Dependent target is defined in `self.targets_as_params`\n        \"\"\"\n        return {}\n\n    @classmethod\n    def get_class_fullname(cls) -> str:\n        return get_shortest_class_fullname(cls)\n\n    @classmethod\n    def is_serializable(cls) -> bool:\n        return True\n\n    def get_base_init_args(self) -> dict[str, Any]:\n        \"\"\"Returns base init args - p\"\"\"\n        return {\"p\": self.p}\n\n    def get_transform_init_args(self) -> dict[str, Any]:\n        \"\"\"Exclude seed from init args during serialization\"\"\"\n        args = {k: getattr(self, k) for k in self.get_transform_init_args_names()}\n        args.pop(\"seed\", None)  # Remove seed from args\n        return args\n\n    def to_dict_private(self) -> dict[str, Any]:\n        state = {\"__class_fullname__\": self.get_class_fullname()}\n        state.update(self.get_base_init_args())\n        state.update(self.get_transform_init_args())\n        return state\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.DualTransform","title":"class DualTransform [view source on GitHub]","text":"

A base class for transformations that should be applied both to an image and its corresponding properties such as masks, bounding boxes, and keypoints. This class ensures that when a transform is applied to an image, all associated entities are transformed accordingly to maintain consistency between the image and its annotations.

Methods

apply(img: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to the image.

img: Input image of shape (H, W, C) or (H, W) for grayscale.\n**params: Additional parameters specific to the transform.\n\nReturns Transformed image of the same shape as input.\n

apply_to_images(images: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple images.

images: Input images of shape (N, H, W, C) or (N, H, W) for grayscale.\n**params: Additional parameters specific to the transform.\n\nReturns Transformed images in the same format as input.\n

apply_to_mask(mask: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a mask.

mask: Input mask of shape (H, W), (H, W, C) for multi-channel masks\n**params: Additional parameters specific to the transform.\n\nReturns Transformed mask in the same format as input.\n

apply_to_masks(masks: np.ndarray, **params: Any) -> np.ndarray | list[np.ndarray]: Apply the transform to multiple masks.

masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n**params: Additional parameters specific to the transform.\nReturns Transformed masks in the same format as input.\n

apply_to_keypoints(keypoints: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to keypoints.

!!! keypoints \"Array of shape (N, 2+) where N is the number of keypoints.\"\n    **params: Additional parameters specific to the transform.\nReturns Transformed keypoints array of shape (N, 2+).\n

apply_to_bboxes(bboxes: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to bounding boxes.

!!! bboxes \"Array of shape (N, 4+) where N is the number of bounding boxes,\"\n        and each row is in the format [x_min, y_min, x_max, y_max].\n**params: Additional parameters specific to the transform.\n\nReturns Transformed bounding boxes array of shape (N, 4+).\n

apply_to_volume(volume: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a volume.

volume: Input volume of shape (D, H, W) or (D, H, W, C).\n**params: Additional parameters specific to the transform.\n\nReturns Transformed volume of the same shape as input.\n

apply_to_volumes(volumes: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple volumes.

volumes: Input volumes of shape (N, D, H, W) or (N, D, H, W, C).\n**params: Additional parameters specific to the transform.\n\nReturns Transformed volumes in the same format as input.\n

apply_to_mask3d(mask: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to a 3D mask.

mask: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n**params: Additional parameters specific to the transform.\n\nReturns Transformed 3D mask in the same format as input.\n

apply_to_masks3d(masks: np.ndarray, **params: Any) -> np.ndarray: Apply the transform to multiple 3D masks.

masks: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n**params: Additional parameters specific to the transform.\n\nReturns Transformed 3D masks in the same format as input.\n

Note

  • All apply_* methods should maintain the input shape and format of the data.
  • When applying transforms to masks, ensure that discrete values (e.g., class labels) are preserved.
  • For keypoints and bounding boxes, the transformation should maintain their relative positions with respect to the transformed image.
  • The difference between apply_to_mask and apply_to_masks is mainly in how they handle 3D arrays: apply_to_mask treats a 3D array as a multi-channel mask, while apply_to_masks treats it as multiple single-channel masks.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class DualTransform(BasicTransform):\n    \"\"\"A base class for transformations that should be applied both to an image and its corresponding properties\n    such as masks, bounding boxes, and keypoints. This class ensures that when a transform is applied to an image,\n    all associated entities are transformed accordingly to maintain consistency between the image and its annotations.\n\n    Methods:\n        apply(img: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to the image.\n\n            img: Input image of shape (H, W, C) or (H, W) for grayscale.\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed image of the same shape as input.\n\n        apply_to_images(images: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple images.\n\n            images: Input images of shape (N, H, W, C) or (N, H, W) for grayscale.\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed images in the same format as input.\n\n        apply_to_mask(mask: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a mask.\n\n            mask: Input mask of shape (H, W), (H, W, C) for multi-channel masks\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed mask in the same format as input.\n\n        apply_to_masks(masks: np.ndarray, **params: Any) -> np.ndarray | list[np.ndarray]:\n            Apply the transform to multiple masks.\n\n            masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n            **params: Additional parameters specific to the transform.\n            Returns Transformed masks in the same format as input.\n\n        apply_to_keypoints(keypoints: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to keypoints.\n\n            keypoints: Array of shape (N, 2+) where N is the number of keypoints.\n                **params: Additional parameters specific to the transform.\n            Returns Transformed keypoints array of shape (N, 2+).\n\n        apply_to_bboxes(bboxes: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to bounding boxes.\n\n            bboxes: Array of shape (N, 4+) where N is the number of bounding boxes,\n                    and each row is in the format [x_min, y_min, x_max, y_max].\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed bounding boxes array of shape (N, 4+).\n\n        apply_to_volume(volume: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a volume.\n\n            volume: Input volume of shape (D, H, W) or (D, H, W, C).\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed volume of the same shape as input.\n\n        apply_to_volumes(volumes: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple volumes.\n\n            volumes: Input volumes of shape (N, D, H, W) or (N, D, H, W, C).\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed volumes in the same format as input.\n\n        apply_to_mask3d(mask: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to a 3D mask.\n\n            mask: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed 3D mask in the same format as input.\n\n        apply_to_masks3d(masks: np.ndarray, **params: Any) -> np.ndarray:\n            Apply the transform to multiple 3D masks.\n\n            masks: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n            **params: Additional parameters specific to the transform.\n\n            Returns Transformed 3D masks in the same format as input.\n\n    Note:\n        - All `apply_*` methods should maintain the input shape and format of the data.\n        - When applying transforms to masks, ensure that discrete values (e.g., class labels) are preserved.\n        - For keypoints and bounding boxes, the transformation should maintain their relative positions\n            with respect to the transformed image.\n        - The difference between `apply_to_mask` and `apply_to_masks` is mainly in how they handle 3D arrays:\n            `apply_to_mask` treats a 3D array as a multi-channel mask, while `apply_to_masks` treats it as\n            multiple single-channel masks.\n\n    \"\"\"\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"mask\": self.apply_to_mask,\n            \"masks\": self.apply_to_masks,\n            \"mask3d\": self.apply_to_mask3d,\n            \"masks3d\": self.apply_to_masks3d,\n            \"bboxes\": self.apply_to_bboxes,\n            \"keypoints\": self.apply_to_keypoints,\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n        }\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        msg = f\"Method apply_to_keypoints is not implemented in class {self.__class__.__name__}\"\n        raise NotImplementedError(msg)\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        raise NotImplementedError(f\"BBoxes not implemented for {self.__class__.__name__}\")\n\n    def apply_to_mask(self, mask: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        return self.apply(mask, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=False)\n    def apply_to_masks(self, masks: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to multiple masks.\n\n        Args:\n            masks: Array of shape (N, H, W) or (N, H, W, C) where N is number of masks\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Array of transformed masks with same shape as input\n        \"\"\"\n        return self.apply(masks, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=False, has_depth_dim=True)\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D mask.\n\n        Args:\n            mask3d: Input 3D mask of shape (D, H, W) or (D, H, W, C)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed 3D mask in the same format as input\n        \"\"\"\n        return self.apply_to_mask(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D masks.\n\n        Args:\n            masks3d: Input 3D masks of shape (N, D, H, W) or (N, D, H, W, C)\n            *args: Additional positional arguments\n            **params: Additional parameters specific to the transform\n\n        Returns:\n            Transformed 3D masks in the same format as input\n        \"\"\"\n        return self.apply_to_mask(masks3d, *args, **params)\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.ImageOnlyTransform","title":"class ImageOnlyTransform [view source on GitHub]","text":"

Transform applied to image only.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class ImageOnlyTransform(BasicTransform):\n    \"\"\"Transform applied to image only.\"\"\"\n\n    _targets = (Targets.IMAGE, Targets.VOLUME)\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n        }\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.NoOp","title":"class NoOp [view source on GitHub]","text":"

Identity transform (does nothing).

Targets

image, mask, bboxes, keypoints, volume, mask3d

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class NoOp(DualTransform):\n    \"\"\"Identity transform (does nothing).\n\n    Targets:\n        image, mask, bboxes, keypoints, volume, mask3d\n    \"\"\"\n\n    _targets = ALL_TARGETS\n\n    def apply_to_keypoints(self, keypoints: np.ndarray, **params: Any) -> np.ndarray:\n        return keypoints\n\n    def apply_to_bboxes(self, bboxes: np.ndarray, **params: Any) -> np.ndarray:\n        return bboxes\n\n    def apply(self, img: np.ndarray, **params: Any) -> np.ndarray:\n        return img\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> np.ndarray:\n        return mask\n\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> np.ndarray:\n        return volume\n\n    def apply_to_volumes(self, volumes: np.ndarray, **params: Any) -> np.ndarray:\n        return volumes\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> np.ndarray:\n        return mask3d\n\n    def apply_to_masks3d(self, masks3d: np.ndarray, **params: Any) -> np.ndarray:\n        return masks3d\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/core/transforms_interface/#albumentations.core.transforms_interface.Transform3D","title":"class Transform3D [view source on GitHub]","text":"

Base class for all 3D transforms.

Transform3D inherits from DualTransform because 3D transforms can be applied to both volumes and masks, similar to how 2D DualTransforms work with images and masks.

Targets

volume: 3D numpy array of shape (D, H, W) or (D, H, W, C) volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C) mask: 3D numpy array of shape (D, H, W) masks: Batch of 3D arrays of shape (N, D, H, W) keypoints: 3D numpy array of shape (N, 3)

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/core/transforms_interface.py Python
class Transform3D(DualTransform):\n    \"\"\"Base class for all 3D transforms.\n\n    Transform3D inherits from DualTransform because 3D transforms can be applied to both\n    volumes and masks, similar to how 2D DualTransforms work with images and masks.\n\n    Targets:\n        volume: 3D numpy array of shape (D, H, W) or (D, H, W, C)\n        volumes: Batch of 3D arrays of shape (N, D, H, W) or (N, D, H, W, C)\n        mask: 3D numpy array of shape (D, H, W)\n        masks: Batch of 3D arrays of shape (N, D, H, W)\n        keypoints: 3D numpy array of shape (N, 3)\n    \"\"\"\n\n    def apply_to_volume(self, volume: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D volume.\"\"\"\n        raise NotImplementedError\n\n    @batch_transform(\"spatial\", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)\n    def apply_to_volumes(self, volumes: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D volumes.\"\"\"\n        return self.apply_to_volume(volumes, *args, **params)\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to single 3D mask.\"\"\"\n        return self.apply_to_volume(mask3d, *args, **params)\n\n    @batch_transform(\"spatial\", keep_depth_dim=True, has_batch_dim=True, has_depth_dim=True)\n    def apply_to_masks3d(self, masks3d: np.ndarray, *args: Any, **params: Any) -> np.ndarray:\n        \"\"\"Apply transform to batch of 3D masks.\"\"\"\n        return self.apply_to_mask3d(masks3d, *args, **params)\n\n    @property\n    def targets(self) -> dict[str, Callable[..., Any]]:\n        \"\"\"Define valid targets for 3D transforms.\"\"\"\n        return {\n            \"volume\": self.apply_to_volume,\n            \"volumes\": self.apply_to_volumes,\n            \"mask3d\": self.apply_to_mask3d,\n            \"masks3d\": self.apply_to_masks3d,\n            \"keypoints\": self.apply_to_keypoints,\n        }\n
"},{"location":"api_reference/pytorch/","title":"Index","text":"
  • Transforms (albumentations.pytorch.transforms)
"},{"location":"api_reference/pytorch/transforms/","title":"Transforms (pytorch.transforms)","text":""},{"location":"api_reference/pytorch/transforms/#albumentations.pytorch.transforms.ToTensor3D","title":"class ToTensor3D (p=1.0, always_apply=None) [view source on GitHub]","text":"

Convert 3D volumes and masks to PyTorch tensors.

This transform is designed for 3D medical imaging data. It converts numpy arrays to PyTorch tensors and ensures consistent channel positioning.

For all inputs (volumes and masks): - Input: (D, H, W, C) or (D, H, W) - depth, height, width, [channels] - Output: (C, D, H, W) - channels first format for PyTorch For single-channel input, adds C=1 dimension

Note

This transform always moves channels to first position as this is the standard PyTorch format. For masks that need to stay in DHWC format, use a different transform or handle the transposition after this transform.

Parameters:

Name Type Description p float

Probability of applying the transform. Default: 1.0

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/pytorch/transforms.py Python
class ToTensor3D(BasicTransform):\n    \"\"\"Convert 3D volumes and masks to PyTorch tensors.\n\n    This transform is designed for 3D medical imaging data. It converts numpy arrays\n    to PyTorch tensors and ensures consistent channel positioning.\n\n    For all inputs (volumes and masks):\n        - Input:  (D, H, W, C) or (D, H, W) - depth, height, width, [channels]\n        - Output: (C, D, H, W) - channels first format for PyTorch\n                 For single-channel input, adds C=1 dimension\n\n    Note:\n        This transform always moves channels to first position as this is\n        the standard PyTorch format. For masks that need to stay in DHWC format,\n        use a different transform or handle the transposition after this transform.\n\n    Args:\n        p (float): Probability of applying the transform. Default: 1.0\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    def __init__(self, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p, always_apply=always_apply)\n\n    @property\n    def targets(self) -> dict[str, Any]:\n        return {\n            \"volume\": self.apply_to_volume,\n            \"mask3d\": self.apply_to_mask3d,\n        }\n\n    def apply_to_volume(self, volume: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert 3D volume to channels-first tensor.\"\"\"\n        if volume.ndim == NUM_VOLUME_DIMENSIONS:  # D,H,W,C\n            return torch.from_numpy(volume.transpose(3, 0, 1, 2))\n        if volume.ndim == NUM_VOLUME_DIMENSIONS - 1:  # D,H,W\n            return torch.from_numpy(volume[np.newaxis, ...])\n        raise ValueError(f\"Expected 3D or 4D array (D,H,W) or (D,H,W,C), got {volume.ndim}D array\")\n\n    def apply_to_mask3d(self, mask3d: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert 3D mask to channels-first tensor.\"\"\"\n        return self.apply_to_volume(mask3d, **params)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return ()\n
"},{"location":"api_reference/pytorch/transforms/#albumentations.pytorch.transforms.ToTensorV2","title":"class ToTensorV2 (transpose_mask=False, p=1.0, always_apply=None) [view source on GitHub]","text":"

Converts images/masks to PyTorch Tensors, inheriting from BasicTransform. For images: - If input is in HWC format, converts to PyTorch CHW format - If input is in HW format, converts to PyTorch 1HW format (adds channel dimension)

Attributes:

Name Type Description transpose_mask bool

If True, transposes 3D input mask dimensions from [height, width, num_channels] to [num_channels, height, width].

p float

Probability of applying the transform. Default: 1.0.

Interactive Tool Available!

Explore this transform visually and adjust parameters interactively using this tool:

Open Tool

Source code in albumentations/pytorch/transforms.py Python
class ToTensorV2(BasicTransform):\n    \"\"\"Converts images/masks to PyTorch Tensors, inheriting from BasicTransform.\n    For images:\n        - If input is in `HWC` format, converts to PyTorch `CHW` format\n        - If input is in `HW` format, converts to PyTorch `1HW` format (adds channel dimension)\n\n    Attributes:\n        transpose_mask (bool): If True, transposes 3D input mask dimensions from `[height, width, num_channels]` to\n            `[num_channels, height, width]`.\n        p (float): Probability of applying the transform. Default: 1.0.\n    \"\"\"\n\n    _targets = (Targets.IMAGE, Targets.MASK)\n\n    def __init__(self, transpose_mask: bool = False, p: float = 1.0, always_apply: bool | None = None):\n        super().__init__(p=p, always_apply=always_apply)\n        self.transpose_mask = transpose_mask\n\n    @property\n    def targets(self) -> dict[str, Any]:\n        return {\n            \"image\": self.apply,\n            \"images\": self.apply_to_images,\n            \"mask\": self.apply_to_mask,\n            \"masks\": self.apply_to_masks,\n        }\n\n    def apply(self, img: np.ndarray, **params: Any) -> torch.Tensor:\n        if img.ndim not in {MONO_CHANNEL_DIMENSIONS, NUM_MULTI_CHANNEL_DIMENSIONS}:\n            msg = \"Albumentations only supports images in HW or HWC format\"\n            raise ValueError(msg)\n\n        if img.ndim == MONO_CHANNEL_DIMENSIONS:\n            img = np.expand_dims(img, 2)\n\n        return torch.from_numpy(img.transpose(2, 0, 1))\n\n    def apply_to_mask(self, mask: np.ndarray, **params: Any) -> torch.Tensor:\n        if self.transpose_mask and mask.ndim == NUM_MULTI_CHANNEL_DIMENSIONS:\n            mask = mask.transpose(2, 0, 1)\n        return torch.from_numpy(mask)\n\n    @overload\n    def apply_to_masks(self, masks: list[np.ndarray], **params: Any) -> list[torch.Tensor]: ...\n\n    @overload\n    def apply_to_masks(self, masks: np.ndarray, **params: Any) -> torch.Tensor: ...\n\n    def apply_to_masks(self, masks: np.ndarray | list[np.ndarray], **params: Any) -> torch.Tensor | list[torch.Tensor]:\n        \"\"\"Convert numpy array or list of numpy array masks to torch tensor(s).\n\n        Args:\n            masks: Numpy array of shape (N, H, W) or (N, H, W, C),\n                or a list of numpy arrays with shape (H, W) or (H, W, C).\n            params: Additional parameters.\n\n        Returns:\n            If transpose_mask is True and input is (N, H, W, C), returns tensor of shape (N, C, H, W).\n            If transpose_mask is True and input is (H, W, C), returns a list of tensors with shape (C, H, W).\n            Otherwise, returns tensors with the same shape as input.\n        \"\"\"\n        if isinstance(masks, list):\n            return [self.apply_to_mask(mask, **params) for mask in masks]\n\n        if self.transpose_mask and masks.ndim == NUM_VOLUME_DIMENSIONS:  # (N, H, W, C)\n            masks = np.transpose(masks, (0, 3, 1, 2))  # -> (N, C, H, W)\n        return torch.from_numpy(masks)\n\n    def apply_to_images(self, images: np.ndarray, **params: Any) -> torch.Tensor:\n        \"\"\"Convert batch of images from (N, H, W, C) to (N, C, H, W).\"\"\"\n        if images.ndim != NUM_VOLUME_DIMENSIONS:  # N,H,W,C\n            raise ValueError(f\"Expected 4D array (N,H,W,C), got {images.ndim}D array\")\n        return torch.from_numpy(images.transpose(0, 3, 1, 2))  # -> (N,C,H,W)\n\n    def get_transform_init_args_names(self) -> tuple[str, ...]:\n        return (\"transpose_mask\",)\n
"},{"location":"autoalbument/","title":"AutoAlbument Overview","text":"

AutoAlbument is an AutoML tool that learns image augmentation policies from data using the Faster AutoAugment algorithm. It relieves the user from manually selecting augmentations and tuning their parameters. AutoAlbument provides a complete ready-to-use configuration for an augmentation pipeline.

AutoAlbument supports image classification and semantic segmentation tasks. The library requires Python 3.6 or higher.

The source code and issue tracker are available at https://github.com/albumentations-team/autoalbument

Table of contents:

  • AutoAlbument introduction and core concepts
  • Installation
  • Benchmarks and a comparison with baseline augmentation strategies
  • How to use AutoAlbument
  • How to use an AutoAlbument Docker image
  • How to use a custom classification or semantic segmentation model
  • Metrics and their meaning
  • Tuning parameters
  • Examples
  • Search algorithms
  • FAQ
"},{"location":"autoalbument/benchmarks/","title":"Benchmarks and a comparison with baseline augmentation strategies","text":"

Here is a comparison between a baseline augmentation strategy and an augmentation policy discovered by AutoAlbument for different classification and semantic segmentation tasks. You can read more about these benchmarks in the autoalbument-benchmarks repository.

"},{"location":"autoalbument/benchmarks/#classification","title":"Classification","text":"Dataset Baseline Top-1 Accuracy AutoAlbument Top-1 Accuracy CIFAR10 91.79 96.02 SVHN 98.31 98.48 ImageNet 73.27 75.17"},{"location":"autoalbument/benchmarks/#semantic-segmentation","title":"Semantic segmentation","text":"Dataset Baseline mIOU AutoAlbument mIOU Pascal VOC 73.34 75.55 Cityscapes 79.47 79.92"},{"location":"autoalbument/custom_model/","title":"How to use a custom classification or semantic segmentation model","text":"

By default AutoAlbument uses pytorch-image-models for classification and segmentation_models.pytorch for semantic segmentation. You can use any model from these packages by providing an appropriate model name.

However, you can also use a custom model with AutoAlbument. To do so, you need to define a Discriminator model. This Discriminator model should have two outputs.

  • The first output should provide a prediction for a classification or semantic segmentation task. For classification, it should output a tensor with a shape [batch_size, num_classes] with logits. For semantic segmentation, it should output a tensor with the shape [batch_size, num_classes, height, width] with logits.

  • The second (auxiliary) output should return a tensor with the shape [batch_size] that contains logits for Discriminator's predictions (whether Discriminator thinks that an image wasn't or was augmented).

To create such a model, you need to subclass the autoalbument.faster_autoaugment.models.BaseDiscriminator class and implement the forward method. This method should take a batch of images, that is, a tensor with the shape [batch_size, num_channels, height, width]. It should return a tuple that contains tensors from the two outputs described above.

As an example, take a look at how default classification and semantic segmentation models are defined in AutoAlbument - https://github.com/albumentations-team/autoalbument/blob/master/autoalbument/faster_autoaugment/models.py or explore an example of a custom model for the CIFAR10 dataset.

Next, you need to specify this custom model in config.yaml, an AutoAlbument config file. AutoAlbument uses the instantiate function from Hydra to instantiate an object. You need to set the _target_ config variable in the classification_model or semantic_segmentation_model section, depending on the task. In this config variable, you need to provide a path to a class with the model. This path should be located inside PYTHONPATH, so Hydra could correctly use it. The simplest way is to define your model in a file such as model.py and place this file in the same directory with dataset.py and search.yaml because this directory is automatically added to PYTHONPATH. Next, you could define _target_ such as _target_: model.MyClassificationModel.

Take a look at the CIFAR10 example config that uses a custom model defined in model.py as a starting point for defining a custom model.

"},{"location":"autoalbument/docker/","title":"How to use an AutoAlbument Docker image","text":"

You can run AutoAlbument from a Docker image. The ghcr.io/albumentations-team/autoalbument:latest Docker image contains the latest release version of AutoAlbument.

You can also use an image that contains a specific version of AutoAlbument. In that case, you need to use the AutoAlbument version as a tag for a Docker image, e.g., the ghcr.io/albumentations-team/autoalbument:0.3.0 image contains AutoAlbument 0.3.0.

The latest AutoAlbument image is based on the pytorch/pytorch:1.7.0-cuda11.0-cudnn8-runtime image.

When you run a Docker container with AutoAlbument, you need to mount a config directory (a directory containing dataset.py and search.yaml files) and other required directories, such as a directory that contains training data.

Here is an example command that runs a Docker container that will search for CIFAR10 augmentation policies.

docker run -it --rm --gpus all --ipc=host -v ~/projects/autoalbument/examples/cifar10:/config -v ~/data:/home/autoalbument/data -u $(id -u ${USER}):$(id -g ${USER}) ghcr.io/albumentations-team/autoalbument:latest

Let's take a look at the arguments:

  • --it. Tell Docker that you run an interactive process. Read more in the Docker documentation.
  • --rm. Automatically clean up a container when it exits. Read more in the Docker documentation.
  • --gpus all. Specify GPUs to use. Read more in the Docker documentation.
  • --ipc=host. Increase shared memory size for PyTorch DataLoader. Read more in the PyTorch documentation.
  • -v ~/projects/autoalbument/examples/cifar10:/config. Mounts the ~/projects/autoalbument/examples/cifar10 directory from the host to the /config directory into the container. This example assumes that you have the AutoAlbument repository in the ~/projects/autoalbument/ directory. Generally speaking, you need to mount a directory containing dataset.py and search.yaml into the /config directory in a container.
  • -v ~/data:/home/autoalbument/data. Mounts the directory ~/data that contains the CIFAR10 dataset into the /home/autoalbument/data directory. You can mount a host directory with a dataset into any container directory, but you need to specify config parameters accordingly. In this example, we mount the directory into /home/autoalbument/data because we set this directory (~/data/cifar10) in the config as a root directory for the dataset. Note that Docker doesn't support tilde expansion for the HOME directory, so we explicitly name HOME directory as /home/autoalbument because autoalbument is a default user inside the container.
  • -u $(id -u ${USER}):$(id -g ${USER}). We use that command to tell Docker to use the host's user ID to run code inside a container. We need this command because AutoAlbument will produce artifacts in the config directory (such as augmentation configs and logs). We need that the host user owns those files (and not root, for example) so you can access them afterward.
  • ghcr.io/albumentations-team/autoalbument:latest is the Docker image's name. latest is a tag for the latest stable release. Alternatively, you can use a tag that specifies an AutoAlbument version, e.g., ghcr.io/albumentations-team/autoalbument:0.3.0.
"},{"location":"autoalbument/faq/","title":"FAQ","text":""},{"location":"autoalbument/faq/#search-takes-a-lot-of-time-how-can-i-speed-it-up","title":"Search takes a lot of time. How can I speed it up?","text":"

Instead of a full training dataset, you can use a reduced version to search for augmentation policies. For example, the authors of Faster AutoAugment used 6000 images from the 120 selected classes to find augmentation policies for ImageNet (while the full dataset for ILSVRC contains 1.2 million images and 1000 classes).

"},{"location":"autoalbument/how_to_use/","title":"How to use AutoAlbument","text":"
  1. You need to create a configuration file with AutoAlbument parameters and a Python file that implements a custom PyTorch Dataset for your data. Next, you need to pass those files to AutoAlbument.
  2. AutoAlbument will use Generative Adversarial Network to discover augmentation policies and then create a file containing those policies.
  3. Finally, you can use Albumentations to load augmentation policies from the file and utilize them in your computer vision pipeline.
"},{"location":"autoalbument/how_to_use/#step-1-create-a-configuration-file-and-a-custom-pytorch-dataset-for-your-data","title":"Step 1. Create a configuration file and a custom PyTorch Dataset for your data","text":""},{"location":"autoalbument/how_to_use/#a-create-a-directory-with-configuration-files","title":"a. Create a directory with configuration files","text":"

Run autoalbument-create --config-dir </path/to/directory> --task <deep_learning_task> --num-classes <num_classes>, e.g. autoalbument-create --config-dir ~/experiments/autoalbument-search-cifar10 --task classification --num-classes 10. - A value for the --config-dir option should contain a path to the directory. AutoAlbument will create this directory and put two files into it: dataset.py and search.yaml (more on them later). - A value for the --task option should contain the name of a deep learning task. Supported values are classification and semantic_segmentation. - A value for the --num-classes option should contain the number of distinct classes in the classification or segmentation dataset.

By default, AutoAlbument creates a search.yaml file that contains only most important configuration parameters. To explore all available parameters you can create a config file that contains them all by providing the --generate-full-config argument, e.g. autoalbument-create --config-dir ~/experiments/autoalbument-search-cifar10 --task classification --num-classes 10 --generate-full-config

"},{"location":"autoalbument/how_to_use/#b-add-implementation-for-__len__-and-__getitem__-methods-in-datasetpy","title":"b. Add implementation for __len__ and __getitem__ methods in dataset.py","text":"

The dataset.py file created at step 1 by autoalbument-create contains stubs for implementing a PyTorch dataset (you can read more about creating custom PyTorch datasets here). You need to add implementation for __len__ and __getitem__ methods (and optionally add the initialization logic if required).

A dataset for a classification task should return an image and a class label. A dataset for a segmentation task should return an image and an associated mask.

"},{"location":"autoalbument/how_to_use/#c-optional-adjust-search-parameters-in-searchyaml","title":"c. [Optional] Adjust search parameters in search.yaml","text":"

You may want to change the parameters that AutoAlbument will use to search for augmentation policies. To do this, you need to edit the search.yaml file created by autoalbument-create at step 1. Each configuration parameter contains a comment that describes the meaning of the setting. Please refer to the \"Tuning the search parameters\" section that includes a description of the most critical parameters.

search.yaml is a Hydra config file. You can use all Hydra features inside it.

"},{"location":"autoalbument/how_to_use/#step-2-use-autoalbument-to-search-for-augmentation-policies","title":"Step 2. Use AutoAlbument to search for augmentation policies.","text":"

To search for augmentation policies, run autoalbument-search --config-dir </path/to/directory>, e.g. autoalbument-search --config-dir ~/experiments/autoalbument-search-cifar10. The value of --config-dir should be the same value that was passed to autoalbument-create at step 1.

autoalbument-search will create a directory with output files (by default the path of the directory will be <config_dir>/outputs/<current_date>/<current_time>, but you can customize it in search.yaml). The policy subdirectory will contain JSON files with policies found at each search phase's epoch.

autoalbument-search is a command wrapped with the @hydra.main decorator from Hydra. You can use all Hydra features when calling this command.

AutoAlbument uses PyTorch to search for augmentation policies. You can speed up the search by using a CUDA-capable GPU.

"},{"location":"autoalbument/how_to_use/#step-3-use-albumentations-to-load-augmentation-policies-and-utilize-them-in-your-training-pipeline","title":"Step 3. Use Albumentations to load augmentation policies and utilize them in your training pipeline.","text":"

AutoAlbument produces a JSON file that contains a configuration for an augmentation pipeline. You can load that JSON file with Albumentations:

Text Only
import albumentations as A\ntransform = A.load(\"/path/to/policy.json\")\n

Then you can use the created augmentation pipeline to augment the input data.

For example, to augment an image for a classification task:

Text Only
transformed = transform(image=image)\ntransformed_image = transformed[\"image\"]\n

To augment an image and a mask for a semantic segmentation task: Text Only

transformed = transform(image=image, mask=mask)\ntransformed_image = transformed[\"image\"]\ntransformed_mask = transformed[\"mask\"]\n

"},{"location":"autoalbument/how_to_use/#additional-resources","title":"Additional resources","text":"
  • You can read more about the most important configuration parameters for AutoAlbument in Tuning the search parameters.

  • To see examples of configuration files and custom PyTorch Datasets, please refer to Examples

  • You can read more about using Albumentations for augmentation in those articles Image augmentation for classification, Mask augmentation for segmentation.

  • Refer to this section of the documentation to get examples of how to use Albumentations with PyTorch and TensorFlow 2.

"},{"location":"autoalbument/installation/","title":"Installation","text":"

AutoAlbument requires Python 3.6 or higher.

"},{"location":"autoalbument/installation/#pypi","title":"PyPI","text":"

To install the latest stable version from PyPI:

pip install -U autoalbument

"},{"location":"autoalbument/installation/#github","title":"GitHub","text":"

To install the latest version from GitHub:

pip install -U git+https://github.com/albumentations-team/autoalbument

"},{"location":"autoalbument/introduction/","title":"AutoAlbument introduction and core concepts","text":""},{"location":"autoalbument/introduction/#what-is-autoalbument","title":"What is AutoAlbument","text":"

AutoAlbument is a tool that automatically searches for the best augmentation policies for your data.

Under the hood, it uses the Faster AutoAugment algorithm. Briefly speaking, the idea is to use a GAN-like architecture in which Generator applies augmentation to some input images, and Discriminator must determine whether an image was or wasn't augmented. This process helps to find augmentation policies that will produce images similar to the original images.

"},{"location":"autoalbument/introduction/#how-to-use-autoalbument","title":"How to use AutoAlbument","text":"

To use AutoAlbument, you need to define two things: a PyTorch Dataset for your data and configuration parameters for AutoAlbument. You can read the detailed instruction in the How to use AutoAlbument article.

Internally AutoAlbument uses PyTorch Lightning for training a GAN and Hydra for handling configuration parameters.

Here are a few things about AutoAlbument and Hydra.

"},{"location":"autoalbument/introduction/#hydra","title":"Hydra","text":"

The main internal configuration file is located at autoalbument/cli/conf/config.yaml

Here is its content:

Text Only
defaults:\n - _version\n - task\n - policy_model: default\n - classification_model: default\n - semantic_segmentation_model: default\n - data: default\n - searcher: default\n - trainer: default\n - optim: default\n - callbacks: default\n - logger: default\n - hydra: default\n - seed\n - search\n

Basically, it includes a bunch of config files with default values. Those config files are split into sets of closely related parameters such as model parameters or optimizer parameters. All default config files are located in their respective directories inside autoalbument/cli/conf

The main config file also includes the search.yaml file, which you will use for overriding default parameters for your specific dataset and task (you can read more about creating the search.yaml file with autoalbument-create in How to use AutoAlbument)

To allow great flexibility, AutoAlbument relies heavily on the instantiate function from Hydra. This function allows to define a path to a Python class in a YAML config (using the _target_ parameter) along with arguments to that class, and Hydra will create an instance of this class with the provided arguments.

As a practice example, if a config contains a definition like this:

Text Only
_target_: autoalbument.faster_autoaugment.models.ClassificationModel\nnum_classes: 10\narchitecture: resnet18\npretrained: False\n

AutoAlbument will translate it approximately to the following call:

Text Only
from autoalbument.faster_autoaugment.models import ClassificationModel\n\nmodel = ClassificationModel(num_classes=10, architecture='resnet18', pretrained=False)\n

By relying on this feature, AutoAlbument allows customizing its behavior without changing the library's internal code.

"},{"location":"autoalbument/introduction/#pytorch-lightning","title":"PyTorch Lightning","text":"

AutoAlbument relies on PyTorch Lightning to train a GAN. In AutoAlbument configs, you can configure PyTorch Lightning by passing the appropriate arguments to Trainer through the trainer config or defining a list of Callbacks through the callbacks config.

"},{"location":"autoalbument/metrics/","title":"Metrics and their meaning","text":"

During the search phase, AutoAlbument outputs four metrics: loss, d_loss, a_loss, and Average Parameter Change (at the end of an epoch).

"},{"location":"autoalbument/metrics/#a_loss","title":"a_loss","text":"

a_loss is a loss for the policy network (or Generator in terms of GAN), which applies augmentations to input images.

"},{"location":"autoalbument/metrics/#d_loss","title":"d_loss","text":"

d_loss is a loss for the Discriminator, the network that tries to guess whether the input image is an augmented or non-augmented one.

"},{"location":"autoalbument/metrics/#loss","title":"loss","text":"

loss is a task-specific loss (CrossEntropyLoss for classification, BCEWithLogitsLoss for semantic segmentation) that acts as a regularizer and prevents the policy network from applying such augmentations that will make an object with class A looks like an object with class B.

"},{"location":"autoalbument/metrics/#average-parameter-change","title":"Average Parameter Change","text":"

Average Parameter Change is a difference between magnitudes of augmentation parameters multiplied by their probabilities at the end of an epoch and the same parameters at the beginning of the epoch. The metric is calculated using the following formula:

  • m' and m are magnitude values for the i-th augmentation at the end and the beginning of the epoch, respectively.
  • p' and p are probability values for the i-th augmentation at the end and the beginning of the epoch, respectively.

The intuition behind this metric is that at the beginning, augmentation parameters are initialized at random, so they are now optimal and prone to heavy change at each epoch. After some time, these parameters should begin to converge, and they should change less at each epoch.

"},{"location":"autoalbument/metrics/#examples-for-metric-values","title":"Examples for metric values","text":"

Below are TensorBoard logs for AutoAlbument on different datasets. The search was performed using AutoAlbument configs from the examples directory.

  • CIFAR10
  • SVHN
  • ImageNet
  • Pascal VOC
  • Cityscapes

As you see, in all these charts, loss is slightly decreasing at each epoch, and a_loss or d_loss could either decrease or increase. Average Parameter Change is usually large at first epochs, but then it starts to decrease. As a rule of thumb, to decide whether you should stop AutoAlbument search and use the resulting policy, you should check that Average Parameter Change is stopped decreasing and started to oscillate, wait for a few more epochs, and use the found policy from that epoch.

In autoalbument-benchmaks, we use AutoAlbument policies produced by the last epoch on these charts.

"},{"location":"autoalbument/search_algorithms/","title":"Search algorithms","text":"

AutoAlbument uses the following algorithms to search for augmentation policies.

"},{"location":"autoalbument/search_algorithms/#faster-autoaugment","title":"Faster AutoAugment","text":"

\"Faster AutoAugment: Learning Augmentation Strategies using Backpropagation\" by Ryuichiro Hataya, Jan Zdenek, Kazuki Yoshizoe, and Hideki Nakayama. Paper | Original implementation

"},{"location":"autoalbument/tuning_parameters/","title":"Tuning the search parameters","text":"

The search.yaml file contains parameters for the search of augmentation policies. Here is an example search.yaml for image classification on the CIFAR-10 dataset, and here is an example search.yaml for semantic segmentation on the Pascal VOC dataset.

"},{"location":"autoalbument/tuning_parameters/#task-specific-model","title":"Task-specific model","text":"

A task-specific model is a model that classifies images for a classification task or outputs masks for a semantic segmentation task. Settings for a task-specific model are defined by either classification_model or semantic_segmentation_model depending on a selected task. Ideally, you should select the same model (the same architecture and the same pretrained weights) that you will use in an actual task. AutoAlbument uses models from PyTorch Image Models and Segmentation models packages for classification and semantic segmentation respectively.

"},{"location":"autoalbument/tuning_parameters/#base-pytorch-parameters","title":"Base PyTorch parameters.","text":"

You may want to adjust the following parameters for a PyTorch pipeline:

  • data.dataloader parameters such as batch_size and num_workers
  • Number of epochs to search for best augmentation policies in optim.epochs.
  • Learning rate for optimizers in optim.main.lr and optim.policy.lr.
"},{"location":"autoalbument/tuning_parameters/#parameters-for-the-augmentations-search","title":"Parameters for the augmentations search.","text":"

Those parameters are defined in policy_model. You may want to tune the following ones:

  • num_sub_policies - number of distinct augmentation sub-policies. A random sub-policy is selected in each iteration, and that sub-policy is applied to input data. The larger number of sub-policies will produce a more diverse set of augmentations. On the other side, the more sub-policies you have, the more time and data you need to tune those sub-policies correctly.

  • num_chunks controls the balance between speed and diversity of augmentations in a search phase. Each batch is split-up into num_chunks chunks, and then a random sub-policy is applied to each chunk separately. The larger the value of num_chunks helps to learn augmentation policies better but simultaneously increases the searching time. Authors of FasterAutoAugment used such values for num_chunks that each chunk consisted of 8 to 16 images.

  • operation_count - the number of augmentation operations that will be applied to each input data instance. For example, operation_count: 1 means that only one operation will be applied to an input image/mask, and operation_count: 4 means that four sequential operations will be applied to each input image/mask. The larger number of operations produces a more diverse set of augmentations but simultaneously increases the searching time.

"},{"location":"autoalbument/tuning_parameters/#preprocessing-transforms","title":"Preprocessing transforms","text":"

If images have different sizes or you want to train a model on image patches, you could define preprocessing transforms (such as Resizing, Cropping, and Padding) in data.preprocessing. Those transforms will always be applied to all input data. Found augmentation policies will also contain those preprocessing transforms.

Note that it is crucial for Policy Model (a model that searches for augmentation parameters) to receive images of the same size that will be used during the training of an actual model. For some augmentations, parameters depend on input data's height and width (for example, hole sizes for the Cutout augmentation).

"},{"location":"autoalbument/examples/cifar10/","title":"Image classification on the CIFAR10 dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/cifar10

"},{"location":"autoalbument/examples/cifar10/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/cifar10/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/cifar10/#modelpy","title":"model.py","text":"Python"},{"location":"autoalbument/examples/cityscapes/","title":"Semantic segmentation on Cityscapes dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/cityscapes

"},{"location":"autoalbument/examples/cityscapes/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/cityscapes/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/imagenet/","title":"Image classification on the ImageNet dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/imagenet

"},{"location":"autoalbument/examples/imagenet/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/imagenet/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/list/","title":"List of examples","text":"
  • Image classification on the CIFAR10 dataset.
  • Image classification on the SVHN dataset.
  • Image classification on the ImageNet dataset.
  • Semantic segmentation on the Pascal VOC dataset.
  • Semantic segmentation on the Cityscapes dataset.

To run the search with an example config:

Bash
autoalbument-search --config-dir </path/to/directory_with_dataset.py_and_search.yaml>\n
"},{"location":"autoalbument/examples/pascal_voc/","title":"Semantic segmentation on the Pascal VOC dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/pascal_voc

"},{"location":"autoalbument/examples/pascal_voc/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/pascal_voc/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/svhn/","title":"Image classification on the SVHN dataset","text":"

The following files are also available on GitHub - https://github.com/albumentations-team/autoalbument/tree/master/examples/svhn

"},{"location":"autoalbument/examples/svhn/#datasetpy","title":"dataset.py","text":"Python"},{"location":"autoalbument/examples/svhn/#searchyaml","title":"search.yaml","text":"YAML"},{"location":"autoalbument/examples/svhn/#modelpy","title":"model.py","text":"Python"},{"location":"contributing/coding_guidelines/","title":"Coding Guidelines","text":"

This document outlines the coding standards and best practices for contributing to Albumentations.

"},{"location":"contributing/coding_guidelines/#important-note-about-guidelines","title":"Important Note About Guidelines","text":"

These guidelines represent our current best practices, developed through experience maintaining and expanding the Albumentations codebase. While some existing code may not strictly follow these standards (due to historical reasons), we are gradually refactoring the codebase to align with these guidelines.

For new contributions:

  • All new code must follow these guidelines
  • All modifications to existing code should move it closer to these standards
  • Pull requests that introduce patterns we're trying to move away from will not be accepted

For existing code:

  • You may encounter patterns that don't match these guidelines (e.g., transforms with \"Random\" prefix or Union types for parameters)
  • These are considered technical debt that we're working to address
  • When modifying existing code, take the opportunity to align it with current standards where possible
"},{"location":"contributing/coding_guidelines/#code-style-and-formatting","title":"Code Style and Formatting","text":""},{"location":"contributing/coding_guidelines/#pre-commit-hooks","title":"Pre-commit Hooks","text":"

We use pre-commit hooks to maintain consistent code quality. These hooks automatically check and format your code before each commit.

  • Install pre-commit if you haven't already:
Bash
pip install pre-commit\npre-commit install\n
  • The hooks will run automatically on git commit. To run manually:
Bash
pre-commit run --files $(find albumentations -type f)\n
"},{"location":"contributing/coding_guidelines/#python-version-and-type-hints","title":"Python Version and Type Hints","text":"
  • Use Python 3.9+ features and syntax
  • Always include type hints using Python 3.10+ typing syntax:
Python
# Correct\ndef transform(self, value: float, range: tuple[float, float]) -> float:\n\n# Incorrect - don't use capital-case types\ndef transform(self, value: float, range: Tuple[float, float]) -> Float:\n
  • Use | instead of Union and for optional types:
Python
# Correct\ndef process(value: int | float | None) -> str:\n\n# Incorrect\ndef process(value: Optional[Union[int, float]) -> str:\n
"},{"location":"contributing/coding_guidelines/#naming-conventions","title":"Naming Conventions","text":""},{"location":"contributing/coding_guidelines/#transform-names","title":"Transform Names","text":"
  • Avoid adding \"Random\" prefix to new transforms
Python
# Correct\nclass Brightness(ImageOnlyTransform):\n\n# Incorrect (historical pattern)\nclass RandomBrightness(ImageOnlyTransform):\n
"},{"location":"contributing/coding_guidelines/#parameter-naming","title":"Parameter Naming","text":"
  • Use _range suffix for interval parameters:
Python
# Correct\nbrightness_range: tuple[float, float]\nshadow_intensity_range: tuple[float, float]\n\n# Incorrect\nbrightness_limit: tuple[float, float]\nshadow_intensity: tuple[float, float]\n
"},{"location":"contributing/coding_guidelines/#standard-parameter-names","title":"Standard Parameter Names","text":"

For transforms that handle gaps or boundaries, use these consistent names:

  • border_mode: Specifies how to handle gaps, not mode or pad_mode
  • fill: Defines how to fill holes (pixel value or method), not fill_value, cval, fill_color, pad_value, pad_cval, value, color
  • fill_mask: Same as fill but for mask filling, not fill_mask_value, fill_mask_color, fill_mask_cval
"},{"location":"contributing/coding_guidelines/#parameter-types-and-ranges","title":"Parameter Types and Ranges","text":""},{"location":"contributing/coding_guidelines/#parameter-definitions","title":"Parameter Definitions","text":"
  • Prefer range parameters over fixed values:
Python
# Correct\ndef __init__(self, brightness_range: tuple[float, float] = (-0.2, 0.2)):\n\n# Avoid\ndef __init__(self, brightness: float = 0.2):\n
"},{"location":"contributing/coding_guidelines/#avoid-union-types-for-parameters","title":"Avoid Union Types for Parameters","text":"
  • Don't use Union[float, tuple[float, float]] for parameters
  • Instead, always use ranges where sampling is needed:
Python
# Correct\nscale_range: tuple[float, float] = (0.5, 1.5)\n\n# Avoid\nscale: float | tuple[float, float] = 1.0\n
  • For fixed values, use same value for both range ends:
Python
brightness_range = (0.1, 0.1)  # Fixed brightness of 0.1\n
"},{"location":"contributing/coding_guidelines/#transform-design-principles","title":"Transform Design Principles","text":""},{"location":"contributing/coding_guidelines/#relative-parameters","title":"Relative Parameters","text":"
  • Prefer parameters that are relative to image dimensions rather than fixed pixel values:
Python
# Correct - relative to image size\ndef __init__(self, crop_size_range: tuple[float, float] = (0.1, 0.3)):\n    # crop_size will be fraction of min(height, width)\n\n# Avoid - fixed pixel values\ndef __init__(self, crop_size_range: tuple[int, int] = (32, 96)):\n    # crop_size will be fixed regardless of image size\n
"},{"location":"contributing/coding_guidelines/#data-type-consistency","title":"Data Type Consistency","text":"
  • Ensure transforms produce consistent results regardless of input data type
  • Use provided decorators to handle type conversions:
  • @uint8_io: For transforms that work with uint8 images
  • @float32_io: For transforms that work with float32 images

The decorators will:

  • Pass through images that are already in the target type without conversion
  • Convert other types as needed and convert back after processing
Python
@uint8_io  # If input is uint8 => use as is; if float32 => convert to uint8, process, convert back\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    # img is guaranteed to be uint8\n    # if input was float32 => result will be converted back to float32\n    # if input was uint8 => result will stay uint8\n    return cv2.blur(img, (3, 3))\n\n@float32_io  # If input is float32 => use as is; if uint8 => convert to float32, process, convert back\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    # img is guaranteed to be float32 in range [0, 1]\n    # if input was uint8 => result will be converted back to uint8\n    # if input was float32 => result will stay float32\n    return img * 0.5\n\n# Avoid - manual type conversion\ndef apply(self, img: np.ndarray, **params) -> np.ndarray:\n    if img.dtype != np.uint8:\n        img = (img * 255).clip(0, 255).astype(np.uint8)\n    result = cv2.blur(img, (3, 3))\n    if img.dtype != np.uint8:\n        result = result.astype(np.float32) / 255\n    return result\n
"},{"location":"contributing/coding_guidelines/#channel-flexibility","title":"Channel Flexibility","text":"
  • Support arbitrary number of channels unless specifically constrained:

```python # Correct - works with any number of channels def apply(self, img: np.ndarray, **params) -> np.ndarray: # img shape is (H, W, C), works for any C return img * self.factor

# Also correct - explicitly requires RGB def apply(self, img: np.ndarray, **params) -> np.ndarray: if img.shape[-1] != 3: raise ValueError(\"Transform requires RGB image\") return rgb_to_hsv(img) # RGB-specific processing

"},{"location":"contributing/coding_guidelines/#random-number-generation","title":"Random Number Generation","text":""},{"location":"contributing/coding_guidelines/#using-random-generators","title":"Using Random Generators","text":"
  • Use class-level random generators instead of direct numpy or random calls:
Python
# Correct\nvalue = self.random_generator.uniform(0, 1, size=image.shape)\nchoice = self.py_random.choice(options)\n\n# Incorrect\nvalue = np.random.uniform(0, 1, size=image.shape)\nchoice = random.choice(options)\n
  • Prefer Python's standard library random over numpy.random:
Python
# Correct - using standard library random (faster)\nvalue = self.py_random.uniform(0, 1)\nchoice = self.py_random.choice(options)\n\n# Use numpy.random only when needed\nvalue = self.random_generator.randint(0, 255, size=image.shape)\n
"},{"location":"contributing/coding_guidelines/#parameter-sampling","title":"Parameter Sampling","text":"
  • Handle all probability calculations in get_params or get_params_dependent_on_data
  • Don't perform random operations in apply_xxx or __init__ methods:
Python
def get_params(self):\n    return {\n        \"brightness\": self.random_generator.uniform(\n            self.brightness_range[0],\n            self.brightness_range[1]\n        )\n    }\n
"},{"location":"contributing/coding_guidelines/#transform-development","title":"Transform Development","text":""},{"location":"contributing/coding_guidelines/#method-definitions","title":"Method Definitions","text":"
  • Don't use default arguments in apply_xxx methods:
Python
# Correct\ndef apply_to_mask(self, mask: np.ndarray, fill_mask: int) -> np.ndarray:\n\n# Incorrect\ndef apply_to_mask(self, mask: np.ndarray, fill_mask: int = 0) -> np.ndarray:\n
"},{"location":"contributing/coding_guidelines/#parameter-generation","title":"Parameter Generation","text":""},{"location":"contributing/coding_guidelines/#using-get_params_dependent_on_data","title":"Using get_params_dependent_on_data","text":"

This method provides access to image shape and target data for parameter generation:

Python
def get_params_dependent_on_data(\n    self,\n    params: dict[str, Any],\n    data: dict[str, Any]\n) -> dict[str, Any]:\n    # Access image shape - always available\n    height, width = params[\"shape\"][:2]\n\n    # Access targets if they were passed to transform\n    image = data.get(\"image\")  # Original image\n    mask = data.get(\"mask\")    # Segmentation mask\n    bboxes = data.get(\"bboxes\")  # Bounding boxes\n    keypoints = data.get(\"keypoints\")  # Keypoint coordinates\n\n    # Example: Calculate parameters based on image size\n    crop_size = min(height, width) // 2\n    center_x = width // 2\n    center_y = height // 2\n\n    return {\n        \"crop_size\": crop_size,\n        \"center\": (center_x, center_y)\n    }\n

The method receives:

  • params: Dictionary containing image metadata, where params[\"shape\"] is always available
  • data: Dictionary containing all targets passed to the transform

Use this method when you need to:

  • Calculate parameters based on image dimensions
  • Access target data for parameter generation
  • Ensure transform parameters are appropriate for the input data
"},{"location":"contributing/coding_guidelines/#parameter-validation-with-initschema","title":"Parameter Validation with InitSchema","text":"

Each transform must include an InitSchema class that inherits from BaseTransformInitSchema. This class is responsible for:

  • Validating input parameters before __init__ execution
  • Converting parameter types if needed
  • Ensuring consistent parameter handling
Python
# Correct - full parameter validation\nclass RandomGravel(ImageOnlyTransform):\n    class InitSchema(BaseTransformInitSchema):\n      slant_range: Annotated[tuple[float, float], AfterValidator(nondecreasing)]\n      brightness_coefficient: float = Field(gt=0, le=1)\n\n\n  def __init__(self, slant_range: tuple[float, float], brightness_coefficient: float, p: float = 0.5):\n      super().__init__(p=p)\n      self.slant_range = slant_range\n      self.brightness_coefficient = brightness_coefficient\n
Python
# Incorrect - missing InitSchema\nclass RandomGravel(ImageOnlyTransform):\n    def __init__(self, slant_range: tuple[float, float], brightness_coefficient: float, p: float = 0.5):\n        super().__init__(p=p)\n        self.slant_range = slant_range\n        self.brightness_coefficient = brightness_coefficient\n
"},{"location":"contributing/coding_guidelines/#coordinate-systems","title":"Coordinate Systems","text":""},{"location":"contributing/coding_guidelines/#image-center-calculations","title":"Image Center Calculations","text":"

The center point calculation differs slightly between targets:

  • For images, masks, and keypoints:
Python
# Correct - using helper function\nfrom albumentations.augmentations.geometric.functional import center\ncenter_x, center_y = center(image_shape)  # Returns ((width-1)/2, (height-1)/2)\n\n# Incorrect - manual calculation might miss the -1\ncenter_x = width / 2  # Wrong!\ncenter_y = height / 2  # Wrong!\n
  • For bounding boxes:
Python
# Correct - using helper function\nfrom albumentations.augmentations.geometric.functional import center_bbox\ncenter_x, center_y = center_bbox(image_shape)  # Returns (width/2, height/2)\n\n# Incorrect - using wrong center calculation\ncenter_x, center_y = center(image_shape)  # Wrong for bboxes!\n

This small difference is crucial for pixel-perfect accuracy. Always use the appropriate helper functions:

  • center() for image, mask, and keypoint transformations
  • center_bbox() for bounding box transformations
"},{"location":"contributing/coding_guidelines/#serialization-compatibility","title":"Serialization Compatibility","text":"
  • Ensure transforms work with both tuples and lists for range parameters
  • Test serialization/deserialization with JSON and YAML formats
"},{"location":"contributing/coding_guidelines/#documentation","title":"Documentation","text":""},{"location":"contributing/coding_guidelines/#docstrings","title":"Docstrings","text":"
  • Use Google-style docstrings
  • Include type information, parameter descriptions, and examples:
Python
def transform(self, image: np.ndarray) -> np.ndarray:\n    \"\"\"Apply brightness transformation to the image.\n\n    Args:\n        image: Input image in RGB format.\n\n    Returns:\n        Transformed image.\n\n    Examples:\n        >>> transform = Brightness(brightness_range=(-0.2, 0.2))\n        >>> transformed = transform(image=image)\n    \"\"\"\n
"},{"location":"contributing/coding_guidelines/#comments","title":"Comments","text":"
  • Add comments for complex logic
  • Explain why, not what (the code shows what)
  • Keep comments up to date with code changes
"},{"location":"contributing/coding_guidelines/#updating-transform-documentation","title":"Updating Transform Documentation","text":"

When adding a new transform or modifying the targets of an existing one, you must update the transforms documentation in the README:

  1. Generate the updated documentation by running:
Bash
python -m tools.make_transforms_docs make\n
  1. This will output a formatted list of all transforms and their supported targets

  2. Update the relevant section in README.md with the new information

  3. Ensure the documentation accurately reflects which targets (image, mask, bboxes, keypoints, etc.) are supported by each transform

This helps maintain accurate and up-to-date documentation about transform capabilities.

"},{"location":"contributing/coding_guidelines/#testing","title":"Testing","text":""},{"location":"contributing/coding_guidelines/#test-coverage","title":"Test Coverage","text":"
  • Write tests for all new functionality
  • Include edge cases and error conditions
  • Ensure reproducibility with fixed random seeds
"},{"location":"contributing/coding_guidelines/#test-organization","title":"Test Organization","text":"
  • Place tests in the appropriate module under tests/
  • Follow existing test patterns and naming conventions
  • Use pytest fixtures when appropriate
"},{"location":"contributing/coding_guidelines/#code-review-guidelines","title":"Code Review Guidelines","text":"

Before submitting your PR:

  1. Run all tests
  2. Run pre-commit hooks
  3. Check type hints
  4. Update documentation if needed
  5. Ensure code follows these guidelines
"},{"location":"contributing/coding_guidelines/#getting-help","title":"Getting Help","text":"

If you have questions about these guidelines:

  1. Join our Discord community
  2. Open a GitHub issue
  3. Ask in your pull request
"},{"location":"contributing/environment_setup/","title":"Setting Up Your Development Environment","text":"

This guide will help you set up your development environment for contributing to Albumentations.

"},{"location":"contributing/environment_setup/#prerequisites","title":"Prerequisites","text":"
  • Python 3.9 or higher
  • Git
  • A GitHub account
"},{"location":"contributing/environment_setup/#step-by-step-setup","title":"Step-by-Step Setup","text":""},{"location":"contributing/environment_setup/#1-fork-and-clone-the-repository","title":"1. Fork and Clone the Repository","text":"
  1. Fork the Albumentations repository on GitHub
  2. Clone your fork locally:
Bash
git clone https://github.com/YOUR_USERNAME/albumentations.git\ncd albumentations\n
"},{"location":"contributing/environment_setup/#2-create-a-virtual-environment","title":"2. Create a Virtual Environment","text":"

Choose the appropriate commands for your operating system:

"},{"location":"contributing/environment_setup/#linux-macos","title":"Linux / macOS","text":"Bash
python3 -m venv env\nsource env/bin/activate\n
"},{"location":"contributing/environment_setup/#windows-cmdexe","title":"Windows (cmd.exe)","text":"Bash
python -m venv env\nenv\\Scripts\\activate.bat\n
"},{"location":"contributing/environment_setup/#windows-powershell","title":"Windows (PowerShell)","text":"Bash
python -m venv env\nenv\\Scripts\\activate.ps1\n
"},{"location":"contributing/environment_setup/#3-install-dependencies","title":"3. Install Dependencies","text":"
  1. Install the project in editable mode:
Bash
pip install -e .\n
  1. Install development dependencies:
Bash
pip install -r requirements-dev.txt\n
"},{"location":"contributing/environment_setup/#4-set-up-pre-commit-hooks","title":"4. Set Up Pre-commit Hooks","text":"

Pre-commit hooks help maintain code quality by automatically checking your changes before each commit.

  1. Install pre-commit:
Bash
pip install pre-commit\n
  1. Set up the hooks:
Bash
pre-commit install\n
  1. (Optional) Run hooks manually on all files:
Bash
pre-commit run --files $(find albumentations -type f)\n
"},{"location":"contributing/environment_setup/#verifying-your-setup","title":"Verifying Your Setup","text":""},{"location":"contributing/environment_setup/#run-tests","title":"Run Tests","text":"

Ensure everything is set up correctly by running the test suite:

Bash
pytest\n
"},{"location":"contributing/environment_setup/#common-issues-and-solutions","title":"Common Issues and Solutions","text":""},{"location":"contributing/environment_setup/#permission-errors","title":"Permission Errors","text":"
  • Linux/macOS: If you encounter permission errors, try using sudo for system-wide installations or consider using --user flag with pip
  • Windows: Run your terminal as administrator if you encounter permission issues
"},{"location":"contributing/environment_setup/#virtual-environment-not-activating","title":"Virtual Environment Not Activating","text":"
  • Ensure you're in the correct directory
  • Check that Python is properly installed and in your system PATH
  • Try creating the virtual environment with the full Python path
"},{"location":"contributing/environment_setup/#import-errors-after-installation","title":"Import Errors After Installation","text":"
  • Verify that you're using the correct virtual environment
  • Confirm that all dependencies were installed successfully
  • Try reinstalling the package in editable mode
"},{"location":"contributing/environment_setup/#next-steps","title":"Next Steps","text":"

After setting up your environment:

  1. Create a new branch for your work
  2. Make your changes
  3. Run tests and pre-commit hooks
  4. Submit a pull request

For more detailed information about contributing, please refer to Coding Guidelines

"},{"location":"contributing/environment_setup/#getting-help","title":"Getting Help","text":"

If you encounter any issues with the setup:

  1. Check our Discord community
  2. Open an issue on GitHub
  3. Review existing issues for similar problems and solutions
"},{"location":"examples/","title":"List of examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Using Albumentations for a semantic segmentation task
  • Using Albumentations to augment keypoints
  • Applying the same augmentation with the same parameters to multiple images, masks, bounding boxes, or keypoints
  • Weather augmentations in Albumentations
  • Example of applying XYMasking transform
  • Example of applying ChromaticAberration transform
  • Example of applying Morphological transform
  • Example of applying D4 transform
  • Example of applying RandomGridShuffle transform
  • Example of applying OverlayElements transform
  • Example of applying TextImage transform
  • Migrating from torchvision to Albumentations
  • Debugging an augmentation pipeline with ReplayCompose
  • How to save and load parameters of an augmentation pipeline
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
  • How to save and load transforms to HuggingFace Hub.
"},{"location":"examples/#examples-of-how-to-use-albumentations-with-different-deep-learning-frameworks","title":"Examples of how to use Albumentations with different deep learning frameworks","text":"
  • PyTorch
  • PyTorch and Albumentations for image classification
  • PyTorch and Albumentations for semantic segmentation
  • TensorFlow 2
  • Using Albumentations with Tensorflow
"},{"location":"external_resources/blog_posts_podcasts_talks/","title":"Blog posts, podcasts, talks, and videos about Albumentations","text":""},{"location":"external_resources/blog_posts_podcasts_talks/#blog-posts","title":"Blog posts","text":"
  • Custom Image Augmentation with Keras. Solving CIFAR-10 with Albumentations and TPU on Google Colab..
  • Road detection using segmentation models and albumentations libraries on Keras.
  • Image Data Augmentation for TensorFlow 2, Keras and PyTorch with Albumentations in Python
  • Explore image augmentations using a convenient tool
  • Image Augmentation using PyTorch and Albumentations
  • Employing the albumentation library in PyTorch workflows. Bonus: Helper for selecting appropriate values!
  • Overview of Albumentations: Open-source library for advanced image augmentations
"},{"location":"external_resources/blog_posts_podcasts_talks/#podcasts-talks-and-videos","title":"Podcasts, talks, and videos","text":"
  • PyConBY 2020: Eugene Khvedchenya - Albumentations: Fast and Flexible image augmentations
  • Albumentations Framework: a fast image augmentations library | Interview with Dr. Vladimir Iglovikov
  • Image Data Augmentation for TensorFlow 2, Keras and PyTorch with Albumentations in Python
  • Bengali.AI competition - Ch 5. Image augmentations using albumentations
  • Albumentations Tutorial for Data Augmentation
"},{"location":"external_resources/books/","title":"Books that mention Albumentations","text":"
  • Deep Learning For Dummies. John Paul Mueller, Luca Massaron. May 2019.
  • Data Science Programming All-in-One For Dummies. John Paul Mueller, Luca Massaron. January 2020.
  • PyTorch Computer Vision Cookbook. Michael Avendi. March 2020.
  • Approaching (Almost) Any Machine Learning Problem. Abhishek Thakur. June 2020.
"},{"location":"external_resources/online_courses/","title":"Online classes that cover Albumentations","text":""},{"location":"external_resources/online_courses/#udemy","title":"Udemy","text":"
  • Modern Computer Vision & Deep Learning with Python & PyTorch
  • Deep Learning for Image Segmentation with Python & Pytorch
  • Deep Learning Masterclass with TensorFlow 2 Over 20 Projects
  • Master Deep Learning for Computer Vision in TensorFlow
  • Deep Learning : Image Classification with Tensorflow in 2024
  • Deep learning with PyTorch | Medical Imaging Competitions
  • Veri Art\u0131r\u0131m\u0131: Albumentations ile Projelerle Veri Art\u0131r\u0131m\u0131
  • Mastering Advanced Representation Learning (CV)
"},{"location":"external_resources/online_courses/#coursera","title":"Coursera","text":"
  • Deep Learning with PyTorch : Image Segmentation
  • Facial Keypoint Detection with PyTorch
  • Deep Learning with PyTorch : Object Localization
  • Aerial Image Segmentation with PyTorch
"},{"location":"getting_started/augmentation_mapping/","title":"Transform Library Comparison Guide","text":"

This guide helps you find equivalent transforms between Albumentations and other popular libraries (torchvision and Kornia).

"},{"location":"getting_started/augmentation_mapping/#key-differences","title":"Key Differences","text":""},{"location":"getting_started/augmentation_mapping/#compared-to-torchvision","title":"Compared to TorchVision","text":"
  • Albumentations operates on numpy arrays (TorchVision uses PyTorch tensors)
  • More parameters for fine-tuning transformations
  • Built-in support for mask augmentation
  • Better handling of bounding boxes and keypoints
"},{"location":"getting_started/augmentation_mapping/#compared-to-kornia","title":"Compared to Kornia","text":"
  • CPU-based numpy operations (Kornia uses GPU tensors)
  • More comprehensive support for detection/segmentation
  • Generally better CPU performance
  • Simpler API for common tasks
"},{"location":"getting_started/augmentation_mapping/#common-transform-mappings","title":"Common Transform Mappings","text":""},{"location":"getting_started/augmentation_mapping/#basic-geometric-transforms","title":"Basic Geometric Transforms","text":"TorchVision Transform Albumentations Equivalent Notes Resize Resize / LongestMaxSize - TorchVision's Resize combines two Albumentations behaviors:\u00a0\u00a01. When given (h,w): equivalent to Albumentations Resize\u00a0\u00a02. When given single int + max_size: similar to LongestMaxSize- Albumentations allows separate interpolation method for masks- TorchVision has antialias parameter, Albumentations doesn't ScaleJitter OneOf + multiple Resize - Can be approximated in Albumentations using OneOf container with multiple Resize transforms- Example: transforms = A.OneOf([ A.Resize(height=int(target_h * scale), width=int(target_w * scale)) for scale in np.linspace(0.1, 2.0, num=20) ])- Not exactly the same as continuous random scaling, but provides similar functionality RandomShortestSize OneOf + SmallestMaxSize - Can be approximated in Albumentations using: transforms = A.OneOf([ A.SmallestMaxSize(max_size=size, max_height=max_size, max_width=max_size) for size in [480, 512, 544, 576, 608] ])- Randomly selects size for shortest side while maintaining aspect ratio- Optional max_size parameter limits longest side- TorchVision has antialias parameter, Albumentations doesn't RandomResize OneOf + Resize - TorchVision: randomly selects single size S between min_size and max_size, sets both width and height to S- No direct equivalent in Albumentations (RandomScale preserves aspect ratio)- Can be approximated using: transforms = A.OneOf([ A.Resize(size, size) for size in range(min_size, max_size + 1, step) ]) RandomCrop RandomCrop - Both perform random cropping with similar core functionality- Key differences:\u00a0\u00a01. TorchVision accepts single int for square crop, Albumentations requires both height and width\u00a0\u00a02. Padding options differ:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports padding parameter for pre-padding\u00a0\u00a0\u00a0\u00a0- Albumentations: offers pad_position parameter ('center', 'top_left', etc.)\u00a0\u00a03. Fill value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports dict mapping for different types\u00a0\u00a0\u00a0\u00a0- Albumentations: separate fill and fill_mask parameters\u00a0\u00a04. Padding modes:\u00a0\u00a0\u00a0\u00a0- TorchVision: 'constant', 'edge', 'reflect', 'symmetric'\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV border modes RandomResizedCrop RandomResizedCrop - Nearly identical functionality and parameters- Key differences:\u00a0\u00a01. TorchVision accepts single int for square output, Albumentations requires (height, width) tuple\u00a0\u00a02. Default values are the same (scale=(0.08, 1.0), ratio=(0.75, 1.3333))\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Separate mask_interpolation parameter\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomIoUCrop RandomSizedBBoxSafeCrop - Both ensure safe cropping with respect to bounding boxes- Key differences:\u00a0\u00a01. TorchVision:\u00a0\u00a0\u00a0\u00a0- Implements exact SSD paper approach\u00a0\u00a0\u00a0\u00a0- Uses IoU-based sampling strategy\u00a0\u00a0\u00a0\u00a0- Requires explicit sanitization of boxes after crop\u00a0\u00a02. Albumentations:\u00a0\u00a0\u00a0\u00a0- Simpler approach ensuring bbox safety\u00a0\u00a0\u00a0\u00a0- Directly specifies target size\u00a0\u00a0\u00a0\u00a0- Automatically handles bbox cleanup- For exact SSD-style cropping, might need custom implementation in Albumentations CenterCrop CenterCrop - Both crop the center part of the input- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: accepts single int for square crop or (height, width) tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: requires separate height and width parameters\u00a0\u00a02. Padding behavior:\u00a0\u00a0\u00a0\u00a0- TorchVision: always pads with 0 if image is smaller\u00a0\u00a0\u00a0\u00a0- Albumentations: optional padding with pad_if_needed\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Configurable padding mode and position\u00a0\u00a0\u00a0\u00a0- Separate fill values for image and mask\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomHorizontalFlip HorizontalFlip - Identical functionality- Both have default probability p=0.5- Only naming difference: TorchVision includes \"Random\" in name RandomVerticalFlip VerticalFlip - Identical functionality- Both have default probability p=0.5- Only naming difference: TorchVision includes \"Random\" in name Pad Pad - Similar core padding functionality- Both support:\u00a0\u00a0- Single int for all sides\u00a0\u00a0- (pad_x, pad_y) for symmetric padding\u00a0\u00a0- (left, top, right, bottom) for per-side padding- Key differences:\u00a0\u00a01. Padding modes:\u00a0\u00a0\u00a0\u00a0- TorchVision: 'constant', 'edge', 'reflect', 'symmetric'\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV border modes\u00a0\u00a02. Fill value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports dict mapping for different types\u00a0\u00a0\u00a0\u00a0- Albumentations: separate fill and fill_mask parameters\u00a0\u00a03. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomZoomOut RandomScale + PadIfNeeded - No direct equivalent in Albumentations- Can be approximated by combining: A.Compose([ A.RandomScale(scale_limit=(0.0, 3.0), p=0.5), # scale_limit=(0.0, 3.0) maps to side_range=(1.0, 4.0) A.PadIfNeeded(min_height=height, min_width=width, border_mode=cv2.BORDER_CONSTANT, value=fill) ])- Key differences:\u00a0\u00a01. TorchVision implements specific SSD paper approach\u00a0\u00a02. Albumentations requires composition of two transforms RandomRotation Rotate - Similar core rotation functionality but with different parameters- Key differences:\u00a0\u00a01. Angle specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: degrees parameter (-degrees, +degrees) or (min, max)\u00a0\u00a0\u00a0\u00a0- Albumentations: limit parameter (-limit, +limit) or (min, max)\u00a0\u00a02. Output size control:\u00a0\u00a0\u00a0\u00a0- TorchVision: expand=True/False\u00a0\u00a0\u00a0\u00a0- Albumentations: crop_border=True/False\u00a0\u00a03. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- Bbox rotation methods ('largest_box' or 'ellipse')\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a04. Center specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: supports custom center point\u00a0\u00a0\u00a0\u00a0- Albumentations: always uses image center RandomAffine Affine - Both support core affine operations (translation, rotation, scale, shear)- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single parameters for each transform\u00a0\u00a0\u00a0\u00a0- Albumentations: more flexible with dict options for x/y axes\u00a0\u00a02. Scale handling:\u00a0\u00a0\u00a0\u00a0- Albumentations adds keep_ratio and balanced_scale\u00a0\u00a0\u00a0\u00a0- Albumentations supports independent x/y scaling\u00a0\u00a03. Translation:\u00a0\u00a0\u00a0\u00a0- TorchVision: fraction only\u00a0\u00a0\u00a0\u00a0- Albumentations: both percent and pixels\u00a0\u00a04. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- fit_output to adjust image plane\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Bbox rotation methods\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomPerspective Perspective - Both apply random perspective transformations- Key differences:\u00a0\u00a01. Distortion control:\u00a0\u00a0\u00a0\u00a0- TorchVision: single distortion_scale (0 to 1)\u00a0\u00a0\u00a0\u00a0- Albumentations: scale tuple for corner movement range\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- Albumentations adds keep_size and fit_output options\u00a0\u00a0\u00a0\u00a0- Can control whether to maintain original size\u00a0\u00a03. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Separate mask interpolation\u00a0\u00a0\u00a0\u00a0- More border modes\u00a0\u00a0\u00a0\u00a0- Better control over output size and fitting ElasticTransform ElasticTransform - Similar core functionality: both apply elastic deformations to images- Key differences:\u00a0\u00a01. Parameters have opposite meanings:\u00a0\u00a0\u00a0\u00a0- TorchVision: alpha (displacement), sigma (smoothness)\u00a0\u00a0\u00a0\u00a0- Albumentations: alpha (smoothness), sigma (displacement)\u00a0\u00a02. Default values reflect this difference:\u00a0\u00a0\u00a0\u00a0- TorchVision: alpha=50.0, sigma=5.0\u00a0\u00a0\u00a0\u00a0- Albumentations: alpha=1.0, sigma=50.0- Note on implementation:\u00a0\u00a0- Albumentations follows Simard et al. 2003 paper more closely:\u00a0\u00a0\u00a0\u00a0- \u03c3 should be ~0.05 * image_size\u00a0\u00a0\u00a0\u00a0- \u03b1 should be proportional to \u03c3- Additional Albumentations features:\u00a0\u00a0- approximate mode\u00a0\u00a0- same_dxdy option\u00a0\u00a0- Choice of noise distribution\u00a0\u00a0- Separate mask interpolation ColorJitter ColorJitter - Similar core functionality: both randomly adjust brightness, contrast, saturation, and hue- Key similarities:\u00a0\u00a01. Same parameter names and meanings\u00a0\u00a02. Same value ranges (e.g., hue should be in [-0.5, 0.5])\u00a0\u00a03. Random order of transformations- Key differences:\u00a0\u00a01. Default values:\u00a0\u00a0\u00a0\u00a0- TorchVision: all None by default\u00a0\u00a0\u00a0\u00a0- Albumentations: defaults to (0.8, 1.2) for brightness/contrast/saturation\u00a0\u00a02. Implementation:\u00a0\u00a0\u00a0\u00a0- TorchVision: uses Pillow\u00a0\u00a0\u00a0\u00a0- Albumentations: uses OpenCV (may produce slightly different results)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Explicit probability parameter p\u00a0\u00a0\u00a0\u00a0- Value saturation instead of uint8 overflow RandomChannelPermutation ChannelShuffle - Both randomly permute image channels- Key similarities:\u00a0\u00a01. Same core functionality\u00a0\u00a02. Work on multi-channel images (typically RGB)- Key differences:\u00a0\u00a01. Naming convention only\u00a0\u00a02. Albumentations adds:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomPhotometricDistort RandomOrder + ColorJitter + ChannelShuffle - TorchVision's transform is from SSD paper, combines:\u00a0\u00a01. Color jittering (brightness, contrast, saturation, hue)\u00a0\u00a02. Random channel permutation- Can be replicated in Albumentations using: A.RandomOrder([ A.ColorJitter(brightness=(0.875, 1.125), contrast=(0.5, 1.5), saturation=(0.5, 1.5), hue=(-0.05, 0.05), p=0.5), A.ChannelShuffle(p=0.5) ]) Grayscale ToGray - Similar core functionality: convert RGB to grayscale- Key differences:\u00a0\u00a01. Output channels:\u00a0\u00a0\u00a0\u00a0- TorchVision: only 1 or 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: supports any number of output channels\u00a0\u00a02. Conversion methods:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method (weighted RGB)\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods via method parameter:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 weighted_average (default, same as TorchVision)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 from_lab, desaturation, average, max, pca\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- More flexible channel handling RGB ToRGB - Similar core functionality: convert to RGB format- Key differences:\u00a0\u00a01. Input handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: accepts 1 or 3 channel inputs\u00a0\u00a0\u00a0\u00a0- Albumentations: only accepts single-channel inputs\u00a0\u00a02. Output channels:\u00a0\u00a0\u00a0\u00a0- TorchVision: always 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable via num_output_channels\u00a0\u00a03. Behavior:\u00a0\u00a0\u00a0\u00a0- TorchVision: converts to RGB if not already RGB\u00a0\u00a0\u00a0\u00a0- Albumentations: strictly grayscale to RGB conversion\u00a0\u00a04. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomGrayscale ToGray - Similar core functionality: convert to grayscale with probability- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- TorchVision: p=0.1\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: always preserves input channels\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable output channels\u00a0\u00a03. Conversion methods:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods with different channel support:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 weighted_average, from_lab: 3-channel only\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 desaturation, average, max, pca: any number of channels\u00a0\u00a04. Channel requirements:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with 1 or 3 channels\u00a0\u00a0\u00a0\u00a0- Albumentations: depends on method chosen GaussianBlur GaussianBlur - Similar core functionality: apply Gaussian blur with random kernel size- Key similarities:\u00a0\u00a01. Both support random kernel sizes\u00a0\u00a02. Both support random sigma values- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: kernel_size (exact size), sigma (range)\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (size range), sigma_limit (range)\u00a0\u00a02. Kernel size constraints:\u00a0\u00a0\u00a0\u00a0- TorchVision: must specify exact size\u00a0\u00a0\u00a0\u00a0- Albumentations: can specify range (3, 7) or auto-compute\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- Auto-computation of kernel size from sigma GaussianNoise GaussNoise - Similar core functionality: add Gaussian noise to images- Key similarities:\u00a0\u00a01. Both support mean and standard deviation parameters- Key differences:\u00a0\u00a01. Parameter ranges:\u00a0\u00a0\u00a0\u00a0- TorchVision: fixed values for mean and sigma\u00a0\u00a0\u00a0\u00a0- Albumentations: ranges for both (std_range, mean_range)\u00a0\u00a02. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: expects float [0,1], has clip option\u00a0\u00a0\u00a0\u00a0- Albumentations: auto-scales based on dtype\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Per-channel noise option\u00a0\u00a0\u00a0\u00a0- Noise scale factor for performance\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomInvert InvertImg - Similar core functionality: invert image colors- Key similarities:\u00a0\u00a01. Both invert pixel values\u00a0\u00a02. Both have default probability of 0.5- Key differences:\u00a0\u00a01. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with [0,1] float tensors\u00a0\u00a0\u00a0\u00a0- Albumentations: auto-handles uint8 (255) and float32 (1.0) RandomPosterize Posterize - Similar core functionality: reduce color bits- Key similarities:\u00a0\u00a01. Both posterize images with probability p=0.5- Key differences:\u00a0\u00a01. Bits specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single fixed value [0-8]\u00a0\u00a0\u00a0\u00a0- Albumentations: flexible options with [1-7] (recommended):\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Single value for all channels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Range (min_bits, max_bits)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Per-channel values [r,g,b]\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 Per-channel ranges [(r_min,r_max), ...]\u00a0\u00a02. Practical range:\u00a0\u00a0\u00a0\u00a0- TorchVision: includes 0 (black) and 8 (unchanged)\u00a0\u00a0\u00a0\u00a0- Albumentations: recommended [1-7] for actual posterization RandomSolarize Solarize - Similar core functionality: invert pixels above threshold- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both invert values above threshold- Key differences:\u00a0\u00a01. Threshold specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single fixed threshold value\u00a0\u00a0\u00a0\u00a0- Albumentations: range via threshold_range\u00a0\u00a02. Value handling:\u00a0\u00a0\u00a0\u00a0- TorchVision: works with raw threshold values\u00a0\u00a0\u00a0\u00a0- Albumentations: uses normalized [0,1] range:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 uint8: multiplied by 255\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 float32: multiplied by 1.0 RandomAdjustSharpness Sharpen - Similar core functionality: adjust image sharpness- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- TorchVision: single sharpness_factor\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 0: blurred\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 1: original\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 2: doubled sharpness\u00a0\u00a0\u00a0\u00a0- Albumentations: more controls:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 alpha: effect visibility [0,1]\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 lightness: contrast control\u00a0\u00a02. Method options:\u00a0\u00a0\u00a0\u00a0- TorchVision: single method\u00a0\u00a0\u00a0\u00a0- Albumentations: two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 'kernel': Laplacian operator\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 'gaussian': blur interpolation RandomAutocontrast AutoContrast Same core functionality with identical parameters (p=0.5) RandomEqualize Equalize - Similar core functionality: histogram equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Additional Albumentations features:\u00a0\u00a0\u00a0\u00a0- Choice of algorithm (cv/pil methods)\u00a0\u00a0\u00a0\u00a0- Per-channel or luminance-based equalization\u00a0\u00a0\u00a0\u00a0- Optional masking support Normalize Normalize - Similar core functionality: normalize image values- Key similarities:\u00a0\u00a01. Both support mean/std normalization- Key differences:\u00a0\u00a01. Normalization options:\u00a0\u00a0\u00a0\u00a0- TorchVision: only (input - mean) / std\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 standard (same as TorchVision)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 image (global stats)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 image_per_channel\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 min_max\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 min_max_per_channel\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- max_pixel_value parameter\u00a0\u00a0\u00a0\u00a0- Probability parameter p RandomErasing Erasing - Similar core functionality: randomly erase image regions- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Same default scale=(0.02, 0.33)\u00a0\u00a03. Same default ratio=(0.3, 3.3)- Key differences:\u00a0\u00a01. Fill value options:\u00a0\u00a0\u00a0\u00a0- TorchVision: number/tuple or 'random'\u00a0\u00a0\u00a0\u00a0- Albumentations: additional options:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 random_uniform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 inpaint_telea\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u2022 inpaint_ns\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Mask fill value option\u00a0\u00a0\u00a0\u00a0- Support for masks, bboxes, keypoints JPEG ImageCompression - Similar core functionality: apply JPEG compression- Key similarities:\u00a0\u00a01. Both use quality range 1-100\u00a0\u00a02. Both support quality ranges- Key differences:\u00a0\u00a01. Compression types:\u00a0\u00a0\u00a0\u00a0- TorchVision: JPEG only\u00a0\u00a0\u00a0\u00a0- Albumentations: JPEG and WebP\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Probability parameter p\u00a0\u00a0\u00a0\u00a0- Default quality range (99, 100)"},{"location":"getting_started/augmentation_mapping/#kornia-to-albumentations","title":"Kornia to Albumentations","text":"Kornia Albumentations Notes ColorJitter ColorJitter - Similar core functionality: randomly adjust brightness, contrast, saturation, and hue- Key similarities:\u00a0\u00a01. Both support same parameters (brightness, contrast, saturation, hue)\u00a0\u00a02. Both allow float or tuple ranges for parameters- Key differences:\u00a0\u00a01. Default values:\u00a0\u00a0\u00a0\u00a0- Albumentations: (0.8, 1.2) for brightness/contrast/saturation\u00a0\u00a0\u00a0\u00a0- Kornia: 0.0 for all parameters\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a03. Note: Kornia recommends using ColorJiggle instead as it follows color theory better RandomAutoContrast AutoContrast - Similar core functionality: enhance image contrast automatically- Key similarities:\u00a0\u00a01. Both stretch intensity range to use full range\u00a0\u00a02. Both preserve relative intensities- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter to control value clipping RandomBoxBlur Blur - Similar core functionality: apply box/average blur to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both apply box/average blur filter- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit parameter for range (e.g., (3, 7))\u00a0\u00a0\u00a0\u00a0- Kornia: fixed kernel_size tuple (default (3, 3))\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- border_type parameter ('reflect', 'replicate', 'circular')\u00a0\u00a0\u00a0\u00a0- normalized parameter for L1 norm control RandomBrightness RandomBrightnessContrast - Different scope:\u00a0\u00a0- Kornia: brightness only\u00a0\u00a0- Albumentations: combines brightness and contrast- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: brightness tuple (default: (1.0, 1.0))\u00a0\u00a0\u00a0\u00a0- Albumentations: brightness_limit (default: (-0.2, 0.2))\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- brightness_by_max parameter for adjustment method\u00a0\u00a0\u00a0\u00a0- ensure_safe_range to prevent overflow/underflow\u00a0\u00a0\u00a0\u00a0- Combined contrast control\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter RandomChannelDropout ChannelDropout - Similar core functionality: randomly drop image channels- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both allow specifying fill value for dropped channels- Key differences:\u00a0\u00a01. Channel drop specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed num_drop_channels (default: 1)\u00a0\u00a0\u00a0\u00a0- Albumentations: flexible channel_drop_range tuple (default: (1, 1))\u00a0\u00a02. Error handling:\u00a0\u00a0\u00a0\u00a0- Albumentations: explicit checks for single-channel images and invalid ranges\u00a0\u00a0\u00a0\u00a0- Kornia: simpler parameter validation RandomChannelShuffle ChannelShuffle - Identical core functionality: randomly shuffle image channels RandomClahe CLAHE - Similar core functionality: apply Contrast Limited Adaptive Histogram Equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both allow configuring grid size and clip limit- Key differences:\u00a0\u00a01. Parameter defaults:\u00a0\u00a0\u00a0\u00a0- Kornia: clip_limit=(40.0, 40.0), grid_size=(8, 8)\u00a0\u00a0\u00a0\u00a0- Albumentations: clip_limit=(1, 4), tile_grid_size=(8, 8)\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- slow_and_differentiable parameter for implementation choice RandomContrast RandomBrightnessContrast - Different scope:\u00a0\u00a0- Kornia: contrast only\u00a0\u00a0- Albumentations: combines brightness and contrast- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: contrast tuple (default: (1.0, 1.0))\u00a0\u00a0\u00a0\u00a0- Albumentations: contrast_limit (default: (-0.2, 0.2))\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- ensure_safe_range to prevent overflow/underflow\u00a0\u00a0\u00a0\u00a0- Combined brightness control\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- clip_output parameter RandomEqualize Equalize - Similar core functionality: apply histogram equalization- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- mode parameter to choose between 'cv' and 'pil' methods\u00a0\u00a0\u00a0\u00a0- by_channels parameter for per-channel or luminance-based equalization\u00a0\u00a0\u00a0\u00a0- mask parameter to selectively apply equalization\u00a0\u00a0\u00a0\u00a0- mask_params for dynamic mask generation RandomGamma RandomGamma - Similar core functionality: apply random gamma correction- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: separate gamma (1.0, 1.0) and gain (1.0, 1.0) tuples\u00a0\u00a0\u00a0\u00a0- Albumentations: single gamma_limit (80, 120) as percentage range\u00a0\u00a02. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- eps parameter to prevent numerical errors RandomGaussianBlur GaussianBlur - Similar core functionality: apply Gaussian blur with random parameters- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support kernel size and sigma parameters- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: requires explicit kernel_size and sigma range\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (default: (3, 7)) and sigma_limit (default: 0)\u00a0\u00a02. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- border_type parameter for padding mode\u00a0\u00a0\u00a0\u00a0- separable parameter for 1D convolution optimization RandomGaussianIllumination Illumination - Similar core functionality: apply illumination effects- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support controlling effect intensity and position- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: Gaussian illumination patterns only\u00a0\u00a0\u00a0\u00a0- Albumentations: Multiple modes (linear, corner, gaussian)\u00a0\u00a02. Parameter ranges:\u00a0\u00a0\u00a0\u00a0- Kornia: gain=(0.01, 0.15), center=(0.1, 0.9), sigma=(0.2, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range=(0.01, 0.2), center_range=(0.1, 0.9), sigma_range=(0.2, 1.0)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- mode parameter for different effect types\u00a0\u00a0\u00a0\u00a0- effect_type for brighten/darken control\u00a0\u00a0\u00a0\u00a0- angle_range for linear gradients\u00a0\u00a04. Additional in Kornia:\u00a0\u00a0\u00a0\u00a0- sign parameter for effect direction RandomGaussianNoise GaussNoise - Similar core functionality: add Gaussian noise to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed mean (default: 0.0) and std (default: 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: ranges via std_range (0.2, 0.44) and mean_range (0.0, 0.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- per_channel parameter for independent channel noise\u00a0\u00a0\u00a0\u00a0- noise_scale_factor for performance optimization\u00a0\u00a0\u00a0\u00a0- Automatic value scaling based on image dtype RandomGrayscale ToGray - Similar core functionality: convert images to grayscale- Key differences:\u00a0\u00a01. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=0.1\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5\u00a0\u00a02. Conversion options:\u00a0\u00a0\u00a0\u00a0- Kornia: customizable rgb_weights for channel mixing\u00a0\u00a0\u00a0\u00a0- Albumentations: multiple method options (weighted_average, from_lab, desaturation, average, max, pca)\u00a0\u00a03. Output control:\u00a0\u00a0\u00a0\u00a0- Kornia: always 3-channel output\u00a0\u00a0\u00a0\u00a0- Albumentations: configurable num_output_channels RandomHue ColorJitter (hue parameter) - Similar core functionality: adjust image hue- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: hue-only transform\u00a0\u00a0\u00a0\u00a0- Albumentations: part of ColorJitter with brightness, contrast, and saturation\u00a0\u00a02. Default values:\u00a0\u00a0\u00a0\u00a0- Kornia: hue=(0.0, 0.0), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: hue=(-0.5, 0.5), p=0.5 RandomInvert InvertImg - Similar core functionality: invert image values- Key differences:\u00a0\u00a01. Maximum value handling:\u00a0\u00a0\u00a0\u00a0- Kornia: configurable via max_val parameter (default: 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: automatically determined by dtype (255 for uint8, 1.0 for float32) RandomJPEG ImageCompression - Similar core functionality: apply image compression- Key differences:\u00a0\u00a01. Compression options:\u00a0\u00a0\u00a0\u00a0- Kornia: JPEG only\u00a0\u00a0\u00a0\u00a0- Albumentations: supports both JPEG and WebP\u00a0\u00a02. Quality specification:\u00a0\u00a0\u00a0\u00a0- Kornia: jpeg_quality (default: 50.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: quality_range (default: (99, 100))\u00a0\u00a03. Default probability:\u00a0\u00a0\u00a0\u00a0- Kornia: p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: p=0.5 RandomLinearCornerIllumination Illumination (corner mode) - Similar core functionality: apply corner illumination effects- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: corner illumination only\u00a0\u00a0\u00a0\u00a0- Albumentations: part of general Illumination transform with multiple modes\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: gain (0.01, 0.2) and sign (-1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range (0.01, 0.2) and effect_type (brighten/darken/both)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Multiple illumination modes (linear, corner, gaussian)\u00a0\u00a0\u00a0\u00a0- More control over effect parameters RandomLinearIllumination Illumination (linear mode) - Similar core functionality: apply linear illumination effects- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: linear illumination only\u00a0\u00a0\u00a0\u00a0- Albumentations: part of general Illumination transform with multiple modes\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: gain (0.01, 0.2) and sign (-1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: intensity_range (0.01, 0.2), effect_type (brighten/darken/both), and angle_range (0, 360)\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Multiple illumination modes (linear, corner, gaussian)\u00a0\u00a0\u00a0\u00a0- Explicit angle control for gradient direction RandomMedianBlur MedianBlur - Similar core functionality: apply median blur filter- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: fixed kernel_size tuple (default: (3, 3))\u00a0\u00a0\u00a0\u00a0- Albumentations: range via blur_limit (default: (3, 7))\u00a0\u00a02. Kernel constraints:\u00a0\u00a0\u00a0\u00a0- Albumentations: enforces odd kernel sizes RandomMotionBlur MotionBlur - Similar core functionality: apply directional motion blur- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support angle and direction control- Key differences:\u00a0\u00a01. Kernel size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: kernel_size as int or tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: blur_limit (default: (3, 7))\u00a0\u00a02. Angle control:\u00a0\u00a0\u00a0\u00a0- Kornia: angle parameter with symmetric range (-angle, angle)\u00a0\u00a0\u00a0\u00a0- Albumentations: angle_range (default: (0, 360))\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- allow_shifted parameter for kernel position control RandomPlanckianJitter PlanckianJitter - Similar core functionality: apply physics-based color temperature variations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support 'blackbody' and 'cied' modes- Key differences:\u00a0\u00a01. Temperature control:\u00a0\u00a0\u00a0\u00a0- Kornia: select_from parameter for discrete jitter selection\u00a0\u00a0\u00a0\u00a0- Albumentations: temperature_limit for continuous range\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- sampling_method parameter ('uniform' or 'gaussian')\u00a0\u00a0\u00a0\u00a0- More detailed control over temperature ranges\u00a0\u00a0\u00a0\u00a0- Better documentation of physics-based effects RandomPlasmaBrightness PlasmaBrightnessContrast - Similar core functionality: apply fractal-based brightness adjustments- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7) and intensity (0.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: brightness_range (-0.3, 0.3), contrast_range (-0.3, 0.3), roughness (default: 3.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Combined brightness and contrast adjustment\u00a0\u00a0\u00a0\u00a0- plasma_size parameter for pattern detail control\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation and documentation RandomPlasmaContrast PlasmaBrightnessContrast - Similar core functionality: apply fractal-based contrast adjustments- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7) only\u00a0\u00a0\u00a0\u00a0- Albumentations: contrast_range (-0.3, 0.3), roughness (default: 3.0), plasma_size (default: 256)\u00a0\u00a02. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: contrast-only adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: combined brightness and contrast adjustment\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation\u00a0\u00a0\u00a0\u00a0- Pattern size control via plasma_size RandomPlasmaShadow PlasmaShadow - Similar core functionality: apply fractal-based shadow effects- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Diamond-Square algorithm for pattern generation- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: roughness (0.1, 0.7), shade_intensity (-1.0, 0.0), shade_quantity (0.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: shadow_intensity_range (0.3, 0.7), plasma_size (default: 256), roughness (default: 3.0)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Pattern size control via plasma_size\u00a0\u00a0\u00a0\u00a0- More intuitive intensity range (0 to 1)\u00a0\u00a0\u00a0\u00a0- More detailed mathematical formulation and documentation RandomPosterize Posterize - Similar core functionality: reduce color bits in image- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both operate on color bit reduction- Key differences:\u00a0\u00a01. Bit specification:\u00a0\u00a0\u00a0\u00a0- Kornia: bits parameter (default: 3) with range (0, 8], can be float or tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: num_bits parameter (default: 4) with range [1, 7], supports multiple formats:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single int for all channels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Tuple for random range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* List for per-channel specification\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* List of tuples for per-channel ranges\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- More flexible channel-wise control\u00a0\u00a0\u00a0\u00a0- More detailed documentation and mathematical background RandomRain RandomRain - Similar core functionality: add rain effects to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Rain parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: number_of_drops (1000, 2000), drop_height (5, 20), drop_width (-5, 5)\u00a0\u00a0\u00a0\u00a0- Albumentations: slant_range (-10, 10), drop_length (20), drop_width (1)\u00a0\u00a02. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- drop_color customization\u00a0\u00a0\u00a0\u00a0- blur_value for atmospheric effect\u00a0\u00a0\u00a0\u00a0- brightness_coefficient for lighting adjustment\u00a0\u00a0\u00a0\u00a0- rain_type presets (drizzle, heavy, torrential)\u00a0\u00a03. Approach:\u00a0\u00a0\u00a0\u00a0- Kornia: Direct drop placement\u00a0\u00a0\u00a0\u00a0- Albumentations: More realistic simulation with slant, blur, and brightness effects RandomRGBShift AdditiveNoise - Similar core functionality: add noise/shifts to image channels- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both can affect individual channels- Key differences:\u00a0\u00a01. Approach:\u00a0\u00a0\u00a0\u00a0- Kornia: Simple RGB channel shifts with individual limits\u00a0\u00a0\u00a0\u00a0- Albumentations: More sophisticated noise generation with multiple distributions\u00a0\u00a02. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: r_shift_limit, g_shift_limit, b_shift_limit (all default: 0.5)\u00a0\u00a0\u00a0\u00a0- Albumentations: Flexible noise configuration with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple noise types (uniform, gaussian, laplace, beta)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Different spatial modes (constant, per_pixel, shared)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Customizable distribution parameters\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Performance optimization options\u00a0\u00a0\u00a0\u00a0- More detailed control over noise distribution\u00a0\u00a0\u00a0\u00a0- Spatial application modes RandomSaltAndPepperNoise SaltAndPepper - Similar core functionality: apply salt and pepper noise to images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use same default parameters:\u00a0\u00a0\u00a0\u00a0- amount (0.01, 0.06)\u00a0\u00a0\u00a0\u00a0- salt_vs_pepper (0.4, 0.6)- Key differences:\u00a0\u00a01. Parameter flexibility:\u00a0\u00a0\u00a0\u00a0- Kornia: Supports single float or tuple for parameters\u00a0\u00a0\u00a0\u00a0- Albumentations: Requires tuples for ranges\u00a0\u00a02. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Detailed mathematical formulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Clear examples for different noise levels\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Implementation notes and edge cases\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* References to academic sources RandomSaturation ColorJitter - Different scope and functionality:- Key differences:\u00a0\u00a01. Scope:\u00a0\u00a0\u00a0\u00a0- Kornia: Saturation-only adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: Combined brightness, contrast, saturation, and hue adjustment\u00a0\u00a02. Default parameters:\u00a0\u00a0\u00a0\u00a0- Kornia: saturation (1.0, 1.0), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: saturation (0.8, 1.2), p=0.5\u00a0\u00a03. Implementation:\u00a0\u00a0\u00a0\u00a0- Kornia: Aligns with PIL/TorchVision implementation\u00a0\u00a0\u00a0\u00a0- Albumentations: Uses OpenCV with noted differences in HSV conversion\u00a0\u00a04. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Brightness adjustment\u00a0\u00a0\u00a0\u00a0- Contrast adjustment\u00a0\u00a0\u00a0\u00a0- Hue adjustment\u00a0\u00a0\u00a0\u00a0- Random order of transformations RandomSharpness Sharpen - Similar core functionality: sharpen images- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single sharpness parameter (default: 0.5)\u00a0\u00a0\u00a0\u00a0- Albumentations: More detailed control with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* alpha (0.2, 0.5) for effect visibility\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* lightness (0.5, 1.0) for contrast\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* method choice ('kernel' or 'gaussian')\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* kernel_size and sigma for gaussian method\u00a0\u00a02. Implementation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: Single approach\u00a0\u00a0\u00a0\u00a0- Albumentations: Two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Kernel-based using Laplacian operator\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Gaussian interpolation\u00a0\u00a03. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides detailed mathematical formulation and references RandomSnow RandomSnow - Similar core functionality: add snow effects to images- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: snow_coefficient (0.5, 0.5), brightness (2, 2), p=1.0\u00a0\u00a0\u00a0\u00a0- Albumentations: snow_point_range (0.1, 0.3), brightness_coeff (2.5), p=0.5\u00a0\u00a02. Implementation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: Single approach\u00a0\u00a0\u00a0\u00a0- Albumentations: Two methods:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"bleach\": Simple pixel value thresholding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"texture\": Advanced snow texture simulation\u00a0\u00a03. Additional in Albumentations:\u00a0\u00a0\u00a0\u00a0- Detailed snow simulation with:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* HSV color space manipulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Gaussian noise for texture\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Depth effect simulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Sparkle effects\u00a0\u00a04. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides detailed mathematical formulation and implementation notes RandomSolarize Solarize - Similar core functionality: invert pixel values above threshold- Key similarities:\u00a0\u00a01. Both have default probability p=0.5- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Two parameters:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* thresholds (default: 0.1) for threshold range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* additions (default: 0.1) for value adjustment\u00a0\u00a0\u00a0\u00a0- Albumentations: Single parameter:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* threshold_range (default: (0.5, 0.5))\u00a0\u00a02. Threshold handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Generates from (0.5 - x, 0.5 + x) for float input\u00a0\u00a0\u00a0\u00a0- Albumentations: Direct range specification, scaled by image type max value\u00a0\u00a03. Documentation:\u00a0\u00a0\u00a0\u00a0- Albumentations provides:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Detailed examples for both uint8 and float32 images\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Clear mathematical formulation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Image type-specific behavior explanation CenterCrop CenterCrop - Similar core functionality: crop center of image- Key similarities:\u00a0\u00a01. Both have default probability p=1.0- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size parameter (int or tuple)\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate height and width parameters\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners for interpolation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* resample mode selection\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* cropping_mode ('slice' or 'resample')\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* pad_if_needed for handling small images\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* border_mode for padding method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* fill and fill_mask for padding values\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* pad_position options\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Supports images, masks, bboxes, and keypoints PadTo PadIfNeeded - Can achieve same core functionality- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Can pad to exact size:\u00a0\u00a0\u00a0\u00a0- Kornia: size=(height, width)\u00a0\u00a0\u00a0\u00a0- Albumentations: min_height=height, min_width=width- Key differences:\u00a0\u00a01. Parameter naming:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size tuple\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate dimension parameters\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simple pad_mode selection\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single pad_value\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible position options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate fill and fill_mask\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Optional divisibility padding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple target support\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomAffine Affine - Similar core functionality: apply affine transformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support rotation, translation, scaling, and shear- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* degrees for rotation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* translate as fraction\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale as tuple\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear in degrees\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More flexible parameter formats\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Supports both percent and pixel translation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Dictionary format for independent axis control\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* fit_output for automatic size adjustment\u00a0\u00a0\u00a0\u00a0* keep_ratio for aspect ratio preservation\u00a0\u00a0\u00a0\u00a0* rotate_method options\u00a0\u00a0\u00a0\u00a0* balanced_scale for even scale distribution\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomCrop RandomCrop - Similar core functionality: randomly crop image patches- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Both support padding if needed- Key differences:\u00a0\u00a01. Size specification:\u00a0\u00a0\u00a0\u00a0- Kornia: Single size tuple (height, width)\u00a0\u00a0\u00a0\u00a0- Albumentations: Separate height and width parameters\u00a0\u00a02. Padding options:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible padding sizes (int, tuple[2], tuple[4])\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Multiple padding modes (constant, reflect, replicate)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single fill value\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler padding interface\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate fill values for image and mask\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Flexible pad positioning\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomElasticTransform ElasticTransform - Similar core functionality: apply elastic deformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use Gaussian smoothing for displacement fields\u00a0\u00a03. Both support independent control of x/y deformations:\u00a0\u00a0\u00a0\u00a0- Kornia: via separate values in sigma/alpha tuples\u00a0\u00a0\u00a0\u00a0- Albumentations: via same_dxdy parameter- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* kernel_size tuple (63, 63)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* sigma tuple (32.0, 32.0)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* alpha tuple (1.0, 1.0)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single sigma (default: 50.0)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single alpha (default: 1.0)\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Control over padding mode\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* approximate mode for faster processing\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Choice of noise distribution\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomErasing Erasing - Similar core functionality: randomly erase rectangular regions- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Same default parameters:\u00a0\u00a0\u00a0\u00a0* scale (0.02, 0.33)\u00a0\u00a0\u00a0\u00a0* ratio (0.3, 3.3)- Key differences:\u00a0\u00a01. Fill value options:\u00a0\u00a0\u00a0\u00a0- Kornia: Simple numeric value (default: 0.0)\u00a0\u00a0\u00a0\u00a0- Albumentations: Rich fill options:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Numeric values\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"random\" per pixel\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"random_uniform\" per region\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"inpaint_telea\" method\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* \"inpaint_ns\" method\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate mask_fill value\u00a0\u00a0\u00a0\u00a0* Support for masks, bboxes, keypoints\u00a0\u00a0\u00a0\u00a0* Inpainting options for more natural-looking results\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomFisheye OpticalDistortion - Similar core functionality: apply optical/fisheye distortion- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support fisheye distortion- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate center_x, center_y for distortion center\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* gamma for distortion strength\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single distort_limit parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* mode selection ('camera' or 'fisheye')\u00a0\u00a02. Distortion models:\u00a0\u00a0\u00a0\u00a0- Kornia: Fisheye only\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Camera matrix model\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fisheye model\u00a0\u00a03. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate interpolation methods for image and mask\u00a0\u00a0\u00a0\u00a0* Support for masks, bboxes, keypoints\u00a0\u00a04. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomHorizontalFlip HorizontalFlip - Similar core functionality: flip image horizontally- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Simple operation with same visual result- Key differences:\u00a0\u00a01. Batch handling:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Additional p_batch parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* same_on_batch option\u00a0\u00a0\u00a0\u00a0- Albumentations: No batch-specific parameters\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomPerspective Perspective - Similar core functionality: apply perspective transformation- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both transform image by moving corners\u00a0\u00a03. Both support different interpolation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: via resample (BILINEAR, NEAREST)\u00a0\u00a0\u00a0\u00a0- Albumentations: via interpolation (INTER_LINEAR, INTER_NEAREST, etc.)- Key differences:\u00a0\u00a01. Distortion control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* distortion_scale (0 to 1, default: 0.5)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* sampling_method ('basic' or 'area_preserving')\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale tuple for corner movement range\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* fit_output option for image capture\u00a0\u00a02. Output handling:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keepdim for batch form\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keep_size for output dimensions\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Border mode and fill options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomResizedCrop RandomResizedCrop - Similar core functionality: crop random patches and resize- Key similarities:\u00a0\u00a01. Both have default probability p=1.0\u00a0\u00a02. Same default parameters:\u00a0\u00a0\u00a0\u00a0* scale (0.08, 1.0)\u00a0\u00a0\u00a0\u00a0* ratio (~0.75, ~1.33)\u00a0\u00a03. Both support different interpolation methods:\u00a0\u00a0\u00a0\u00a0- Kornia: via resample\u00a0\u00a0\u00a0\u00a0- Albumentations: via interpolation- Key differences:\u00a0\u00a01. Implementation options:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* cropping_mode ('slice' or 'resample')\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* keepdim for batch form\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fallback to center crop after 10 attempts\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomRotation90 RandomRotate90 - Similar core functionality: rotate image by 90 degrees- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both rotate in 90-degree increments- Key differences:\u00a0\u00a01. Rotation control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* times parameter to specify range of rotations\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* resample and align_corners for interpolation\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler implementation (0-3 rotations)\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomRotation Rotate - Similar core functionality: rotate image by random angle- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support different interpolation methods- Key differences:\u00a0\u00a01. Angle specification:\u00a0\u00a0\u00a0\u00a0- Kornia: degrees parameter (if single value, range is (-degrees, +degrees))\u00a0\u00a0\u00a0\u00a0- Albumentations: limit parameter (default: (-90, 90))\u00a0\u00a02. Additional features:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners for interpolation\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Border mode options\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fill values for padding\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* rotate_method for bboxes\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* crop_border option\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints RandomShear Affine (shear parameter) - Similar core functionality: apply shear transformation- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both support different interpolation methods\u00a0\u00a03. Both support independent x/y shear control- Key differences:\u00a0\u00a01. Parameter specification:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Dedicated shear transform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear parameter supports float, tuple(2), or tuple(4)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simple padding modes (zeros, border, reflection)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Part of general Affine transform\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* shear supports number, tuple, or dict format\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More border modes and fill options\u00a0\u00a02. Additional features in Albumentations:\u00a0\u00a0\u00a0\u00a0* Separate mask interpolation\u00a0\u00a0\u00a0\u00a0* fit_output option\u00a0\u00a0\u00a0\u00a0* Combined with other affine transforms\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomThinPlateSpline ThinPlateSpline - Similar core functionality: apply smooth, non-rigid deformations- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Both use thin plate spline algorithm\u00a0\u00a03. Both support interpolation options- Key differences:\u00a0\u00a01. Deformation control:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Single scale parameter (default: 0.2)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Fixed control point grid\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* scale_range tuple for range of deformation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Configurable num_control_points\u00a0\u00a02. Implementation details:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* align_corners parameter\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Binary mode choice (bilinear/nearest)\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* OpenCV interpolation flags\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* More granular control over grid\u00a0\u00a03. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, keypoints, bboxes RandomVerticalFlip VerticalFlip - Similar core functionality: flip image vertically- Key similarities:\u00a0\u00a01. Both have default probability p=0.5\u00a0\u00a02. Simple operation with same visual result- Key differences:\u00a0\u00a01. Implementation:\u00a0\u00a0\u00a0\u00a0- Kornia:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Additional p_batch parameter\u00a0\u00a0\u00a0\u00a0- Albumentations:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0* Simpler implementation\u00a0\u00a02. Target handling:\u00a0\u00a0\u00a0\u00a0- Kornia: Image tensors only\u00a0\u00a0\u00a0\u00a0- Albumentations: Images, masks, bboxes, keypoints"},{"location":"getting_started/augmentation_mapping/#key-differences_1","title":"Key Differences","text":""},{"location":"getting_started/augmentation_mapping/#compared-to-torchvision_1","title":"Compared to TorchVision","text":"
  • Albumentations operates on numpy arrays instead of PyTorch tensors
  • Albumentations typically provides more parameters for fine-tuning transformations
  • Most Albumentations transforms support both image and mask augmentation
  • Better support for bounding box and keypoint augmentation
"},{"location":"getting_started/augmentation_mapping/#compared-to-kornia_1","title":"Compared to Kornia","text":"
  • Kornia operates directly on GPU tensors, while Albumentations works with numpy arrays
  • Albumentations provides more comprehensive support for object detection and segmentation tasks
  • Albumentations typically offers better performance for CPU-based augmentations
"},{"location":"getting_started/augmentation_mapping/#performance-comparison","title":"Performance Comparison","text":"

According to benchmarking results, Albumentations generally offers superior CPU performance compared to TorchVision and Kornia for most transforms. Here are some key highlights: Common Transforms Performance (images/second, higher is better)

Transform Albumentations TorchVision Kornia Notes HorizontalFlip 8,618 914 390 Albumentations is ~9x faster than TorchVision, ~22x faster than Kornia VerticalFlip 22,847 3,198 1,212 Albumentations is ~7x faster than TorchVision, ~19x faster than Kornia RandomResizedCrop 2,828 511 287 Albumentations is ~5.5x faster than TorchVision, ~10x faster than Kornia Normalize 1,196 519 626 Albumentations is ~2x faster than both ColorJitter 628 46 55 Albumentations is ~13x faster than both"},{"location":"getting_started/augmentation_mapping/#key-performance-insights","title":"Key Performance Insights:","text":"
  • Basic Operations: Albumentations excels at basic transforms like flips and crops, often being 5-20x faster than alternatives
  • Complex Operations: For more complex transforms like elastic deformation, the performance gap narrows
  • Memory Efficiency: Working with numpy arrays (Albumentations) is generally more memory efficient than tensor operations (Kornia/TorchVision) on CPU
"},{"location":"getting_started/augmentation_mapping/#when-to-choose-each-library","title":"When to Choose Each Library:","text":"
  • Albumentations: Best choice for CPU-based preprocessing pipelines and when maximum performance is needed
  • Kornia: Consider when doing augmentation on GPU with existing PyTorch tensors
  • TorchVision: Good choice when deeply integrated into PyTorch ecosystem and GPU performance isn't critical

Note: Benchmarks performed on macOS-15.0.1-arm64 with Python 3.12.7. Your results may vary based on hardware and setup.

"},{"location":"getting_started/augmentation_mapping/#code-examples","title":"Code Examples","text":""},{"location":"getting_started/augmentation_mapping/#torchvision-to-albumentations","title":"TorchVision to Albumentations","text":"Python
# TorchVision\ntransforms = T.Compose([\n    T.RandomHorizontalFlip(p=0.5),\n    T.RandomRotation(10),\n    T.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n\n# Albumentations equivalent\ntransforms = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.Rotate(limit=10),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n
"},{"location":"getting_started/augmentation_mapping/#kornia-to-albumentations_1","title":"Kornia to Albumentations","text":"Python
# Kornia\ntransforms = K.AugmentationSequential(\n    K.RandomHorizontalFlip(p=0.5),\n    K.RandomRotation(degrees=10),\n    K.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n)\n\n# Albumentations equivalent\ntransforms = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.Rotate(limit=10),\n    A.Normalize(mean=[0.485, 0.456, 0.406],\n                std=[0.229, 0.224, 0.225])\n])\n
"},{"location":"getting_started/augmentation_mapping/#additional-resources","title":"Additional Resources","text":"
  • TorchVision Transforms Documentation
  • Kornia Augmentation Documentation
  • Albumentations Documentation
"},{"location":"getting_started/bounding_boxes_augmentation/","title":"Bounding boxes augmentation for object detection","text":""},{"location":"getting_started/bounding_boxes_augmentation/#different-annotations-formats","title":"Different annotations formats","text":"

Bounding boxes are rectangles that mark objects on an image. There are multiple formats of bounding boxes annotations. Each format uses its specific representation of bounding boxes coordinates. Albumentations supports four formats: pascal_voc, albumentations, coco, and yolo .

Let's take a look at each of those formats and how they represent coordinates of bounding boxes.

As an example, we will use an image from the dataset named Common Objects in Context. It contains one bounding box that marks a cat. The image width is 640 pixels, and its height is 480 pixels. The width of the bounding box is 322 pixels, and its height is 117 pixels.

The bounding box has the following (x, y) coordinates of its corners: top-left is (x_min, y_min) or (98px, 345px), top-right is (x_max, y_min) or (420px, 345px), bottom-left is (x_min, y_max) or (98px, 462px), bottom-right is (x_max, y_max) or (420px, 462px). As you see, coordinates of the bounding box's corners are calculated with respect to the top-left corner of the image which has (x, y) coordinates (0, 0).

An example image with a bounding box from the COCO dataset

"},{"location":"getting_started/bounding_boxes_augmentation/#pascal_voc","title":"pascal_voc","text":"

pascal_voc is a format used by the Pascal VOC dataset. Coordinates of a bounding box are encoded with four values in pixels: [x_min, y_min, x_max, y_max]. x_min and y_min are coordinates of the top-left corner of the bounding box. x_max and y_max are coordinates of bottom-right corner of the bounding box.

Coordinates of the example bounding box in this format are [98, 345, 420, 462].

"},{"location":"getting_started/bounding_boxes_augmentation/#albumentations","title":"albumentations","text":"

albumentations is similar to pascal_voc, because it also uses four values [x_min, y_min, x_max, y_max] to represent a bounding box. But unlike pascal_voc, albumentations uses normalized values. To normalize values, we divide coordinates in pixels for the x- and y-axis by the width and the height of the image.

Coordinates of the example bounding box in this format are [98 / 640, 345 / 480, 420 / 640, 462 / 480] which are [0.153125, 0.71875, 0.65625, 0.9625].

Albumentations uses this format internally to work with bounding boxes and augment them.

"},{"location":"getting_started/bounding_boxes_augmentation/#coco","title":"coco","text":"

coco is a format used by the Common Objects in Context COCO dataset.

In coco, a bounding box is defined by four values in pixels [x_min, y_min, width, height]. They are coordinates of the top-left corner along with the width and height of the bounding box.

Coordinates of the example bounding box in this format are [98, 345, 322, 117].

"},{"location":"getting_started/bounding_boxes_augmentation/#yolo","title":"yolo","text":"

In yolo, a bounding box is represented by four values [x_center, y_center, width, height]. x_center and y_center are the normalized coordinates of the center of the bounding box. To make coordinates normalized, we take pixel values of x and y, which marks the center of the bounding box on the x- and y-axis. Then we divide the value of x by the width of the image and value of y by the height of the image. width and height represent the width and the height of the bounding box. They are normalized as well.

Coordinates of the example bounding box in this format are [((420 + 98) / 2) / 640, ((462 + 345) / 2) / 480, 322 / 640, 117 / 480] which are [0.4046875, 0.840625, 0.503125, 0.24375].

How different formats represent coordinates of a bounding box

"},{"location":"getting_started/bounding_boxes_augmentation/#bounding-boxes-augmentation","title":"Bounding boxes augmentation","text":"

Just like with images and masks augmentation, the process of augmenting bounding boxes consists of 4 steps.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and bounding boxes from the disk.
  4. You pass an image and bounding boxes to the augmentation pipeline and receive augmented images and boxes.

Note

Some transforms in Albumentation don't support bounding boxes. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment bounding boxes.

"},{"location":"getting_started/bounding_boxes_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"Python
import albumentations as A\nimport cv2\n
"},{"location":"getting_started/bounding_boxes_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

Here an example of a minimal declaration of an augmentation pipeline that works with bounding boxes.

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco'))\n

Note that unlike image and masks augmentation, Compose now has an additional parameter bbox_params. You need to pass an instance of A.BboxParams to that argument. A.BboxParams specifies settings for working with bounding boxes. format sets the format for bounding boxes coordinates.

It can either be pascal_voc, albumentations, coco or yolo. This value is required because Albumentation needs to know the coordinates' source format for bounding boxes to apply augmentations correctly.

Besides format, A.BboxParams supports a few more settings.

Here is an example of Compose that shows all available settings with A.BboxParams:

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco', min_area=1024, min_visibility=0.1, label_fields=['class_labels']))\n
"},{"location":"getting_started/bounding_boxes_augmentation/#min_area-and-min_visibility","title":"min_area and min_visibility","text":"

min_area and min_visibility parameters control what Albumentations should do to the augmented bounding boxes if their size has changed after augmentation. The size of bounding boxes could change if you apply spatial augmentations, for example, when you crop a part of an image or when you resize an image.

min_area is a value in pixels. If the area of a bounding box after augmentation becomes smaller than min_area, Albumentations will drop that box. So the returned list of augmented bounding boxes won't contain that bounding box.

min_visibility is a value between 0 and 1. If the ratio of the bounding box area after augmentation to the area of the bounding box before augmentation becomes smaller than min_visibility, Albumentations will drop that box. So if the augmentation process cuts the most of the bounding box, that box won't be present in the returned list of the augmented bounding boxes.

Here is an example image that contains two bounding boxes. Bounding boxes coordinates are declared using the coco format.

An example image with two bounding boxes

First, we apply the CenterCrop augmentation without declaring parameters min_area and min_visibility. The augmented image contains two bounding boxes.

An example image with two bounding boxes after applying augmentation

Next, we apply the same CenterCrop augmentation, but now we also use the min_area parameter. Now, the augmented image contains only one bounding box, because the other bounding box's area after augmentation became smaller than min_area, so Albumentations dropped that bounding box.

An example image with one bounding box after applying augmentation with 'min_area'

Finally, we apply the CenterCrop augmentation with the min_visibility. After that augmentation, the resulting image doesn't contain any bounding box, because visibility of all bounding boxes after augmentation are below threshold set by min_visibility.

An example image with zero bounding boxes after applying augmentation with 'min_visibility'

"},{"location":"getting_started/bounding_boxes_augmentation/#class-labels-for-bounding-boxes","title":"Class labels for bounding boxes","text":"

Besides coordinates, each bounding box should have an associated class label that tells which object lies inside the bounding box. There are two ways to pass a label for a bounding box.

Let's say you have an example image with three objects: dog, cat, and sports ball. Bounding boxes coordinates in the coco format for those objects are [23, 74, 295, 388], [377, 294, 252, 161], and [333, 421, 49, 49].

An example image with 3 bounding boxes from the COCO dataset

"},{"location":"getting_started/bounding_boxes_augmentation/#1-you-can-pass-labels-along-with-bounding-boxes-coordinates-by-adding-them-as-additional-values-to-the-list-of-coordinates","title":"1. You can pass labels along with bounding boxes coordinates by adding them as additional values to the list of coordinates.","text":"

For the image above, bounding boxes with class labels will become [23, 74, 295, 388, 'dog'], [377, 294, 252, 161, 'cat'], and [333, 421, 49, 49, 'sports ball'].

Class labels could be of any type: integer, string, or any other Python data type. For example, integer values as class labels will look the following: [23, 74, 295, 388, 18], [377, 294, 252, 161, 17], and [333, 421, 49, 49, 37].

Also, you can use multiple class values for each bounding box, for example [23, 74, 295, 388, 'dog', 'animal'], [377, 294, 252, 161, 'cat', 'animal'], and [333, 421, 49, 49, 'sports ball', 'item'].

"},{"location":"getting_started/bounding_boxes_augmentation/#2you-can-pass-labels-for-bounding-boxes-as-a-separate-list-the-preferred-way","title":"2.You can pass labels for bounding boxes as a separate list (the preferred way).","text":"

For example, if you have three bounding boxes like [23, 74, 295, 388], [377, 294, 252, 161], and [333, 421, 49, 49] you can create a separate list with values like ['cat', 'dog', 'sports ball'], or [18, 17, 37] that contains class labels for those bounding boxes. Next, you pass that list with class labels as a separate argument to the transform function. Albumentations needs to know the names of all those lists with class labels to join them with augmented bounding boxes correctly. Then, if a bounding box is dropped after augmentation because it is no longer visible, Albumentations will drop the class label for that box as well. Use label_fields parameter to set names for all arguments in transform that will contain label descriptions for bounding boxes (more on that in Step 4).

"},{"location":"getting_started/bounding_boxes_augmentation/#step-3-read-images-and-bounding-boxes-from-the-disk","title":"Step 3. Read images and bounding boxes from the disk.","text":"

Read an image from the disk.

Python
image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n

Bounding boxes can be stored on the disk in different serialization formats: JSON, XML, YAML, CSV, etc. So the code to read bounding boxes depends on the actual format of data on the disk.

After you read the data from the disk, you need to prepare bounding boxes for Albumentations.

Albumentations expects that bounding boxes will be represented as a list of lists. Each list contains information about a single bounding box. A bounding box definition should have at list four elements that represent the coordinates of that bounding box. The actual meaning of those four values depends on the format of bounding boxes (either pascal_voc, albumentations, coco, or yolo). Besides four coordinates, each definition of a bounding box may contain one or more extra values. You can use those extra values to store additional information about the bounding box, such as a class label of the object inside the box. During augmentation, Albumentations will not process those extra values. The library will return them as is along with the updated coordinates of the augmented bounding box.

"},{"location":"getting_started/bounding_boxes_augmentation/#step-4-pass-an-image-and-bounding-boxes-to-the-augmentation-pipeline-and-receive-augmented-images-and-boxes","title":"Step 4. Pass an image and bounding boxes to the augmentation pipeline and receive augmented images and boxes.","text":"

As discussed in Step 2, there are two ways of passing class labels along with bounding boxes coordinates:

"},{"location":"getting_started/bounding_boxes_augmentation/#1-pass-class-labels-along-with-coordinates","title":"1. Pass class labels along with coordinates","text":"

So, if you have coordinates of three bounding boxes that look like this:

Python
bboxes = [\n    [23, 74, 295, 388],\n    [377, 294, 252, 161],\n    [333, 421, 49, 49],\n]\n

you can add a class label for each bounding box as an additional element of the list along with four coordinates. So now a list with bounding boxes and their coordinates will look the following:

Python
bboxes = [\n    [23, 74, 295, 388, 'dog'],\n    [377, 294, 252, 161, 'cat'],\n    [333, 421, 49, 49, 'sports ball'],\n]\n

or with multiple labels per each bounding box: Python

bboxes = [\n    [23, 74, 295, 388, 'dog', 'animal'],\n    [377, 294, 252, 161, 'cat', 'animal'],\n    [333, 421, 49, 49, 'sports ball', 'item'],\n]\n

You can use any data type for declaring class labels. It can be string, integer, or any other Python data type.

Next, you pass an image and bounding boxes for it to the transform function and receive the augmented image and bounding boxes.

Python
transformed = transform(image=image, bboxes=bboxes)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\n

Example input and output data for bounding boxes augmentation

"},{"location":"getting_started/bounding_boxes_augmentation/#2-pass-class-labels-in-a-separate-argument-to-transform-the-preferred-way","title":"2. Pass class labels in a separate argument to transform (the preferred way).","text":"

Let's say you have coordinates of three bounding boxes Python

bboxes = [\n    [23, 74, 295, 388],\n    [377, 294, 252, 161],\n    [333, 421, 49, 49],\n]\n

You can create a separate list that contains class labels for those bounding boxes:

Python
class_labels = ['cat', 'dog', 'parrot']\n

Then you pass both bounding boxes and class labels to transform. Note that to pass class labels, you need to use the name of the argument that you declared in label_fields when creating an instance of Compose in step 2. In our case, we set the name of the argument to class_labels.

Python
transformed = transform(image=image, bboxes=bboxes, class_labels=class_labels)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\ntransformed_class_labels = transformed['class_labels']\n

Example input and output data for bounding boxes augmentation with a separate argument for class labels

Note that label_fields expects a list, so you can set multiple fields that contain labels for your bounding boxes. So if you declare Compose like

Python
transform = A.Compose([\n    A.RandomCrop(width=450, height=450),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], bbox_params=A.BboxParams(format='coco', label_fields=['class_labels', 'class_categories'])))\n

you can use those multiple arguments to pass info about class labels, like

Python
class_labels = ['cat', 'dog', 'parrot']\nclass_categories = ['animal', 'animal', 'item']\n\ntransformed = transform(image=image, bboxes=bboxes, class_labels=class_labels, class_categories=class_categories)\ntransformed_image = transformed['image']\ntransformed_bboxes = transformed['bboxes']\ntransformed_class_labels = transformed['class_labels']\ntransformed_class_categories = transformed['class_categories']\n
"},{"location":"getting_started/bounding_boxes_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations to augment bounding boxes for object detection tasks
  • How to use Albumentations for detection tasks if you need to keep all bounding boxes
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/image_augmentation/","title":"Image augmentation for classification","text":"

We can divide the process of image augmentation into four steps:

  1. Import albumentations and a library to read images from the disk (e.g., OpenCV).
  2. Define an augmentation pipeline.
  3. Read images from the disk.
  4. Pass images to the augmentation pipeline and receive augmented images.
"},{"location":"getting_started/image_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"
  • Import Albumentations
Python
import albumentations as A\n
  • Import a library to read images from the disk. In this example, we will use OpenCV. It is an open-source computer vision library that supports many image formats. Albumentations has OpenCV as a dependency, so you already have OpenCV installed.
Python
import cv2\n
"},{"location":"getting_started/image_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

To define an augmentation pipeline, you need to create an instance of the Compose class. As an argument to the Compose class, you need to pass a list of augmentations you want to apply. A call to Compose will return a transform function that will perform image augmentation.

Let's look at an example:

Python
transform = A.Compose([\n    A.RandomCrop(width=256, height=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n

In the example, Compose receives a list with three augmentations: A.RandomCrop, A.HorizontalFlip, and A.RandomBrighntessContrast. You can find the full list of all available augmentations in the GitHub repository and in the API Docs. A demo playground that demonstrates how augmentations will transform the input image is available at https://explore.albumentations.ai.

To create an augmentation, you create an instance of the required augmentation class and pass augmentation parameters to it. A.RandomCrop receives two parameters, height and width. A.RandomCrop(width=256, height=256) means that A.RandomCrop will take an input image, extract a random patch with size 256 by 256 pixels from it and then pass the result to the next augmentation in the pipeline (in this case to A.HorizontalFlip).

A.HorizontalFlip in this example has one parameter named p. p is a special parameter that is supported by almost all augmentations. It controls the probability of applying the augmentation. p=0.5 means that with a probability of 50%, the transform will flip the image horizontally, and with a probability of 50%, the transform won't modify the input image.

A.RandomBrighntessContrast in the example also has one parameter, p. With a probability of 20%, this augmentation will change the brightness and contrast of the image received from A.HorizontalFlip. And with a probability of 80%, it will keep the received image unchanged.

A visualized version of the augmentation pipeline. You pass an image to it, the image goes through all transformations, and then you receive an augmented image from the pipeline.

"},{"location":"getting_started/image_augmentation/#step-3-read-images-from-the-disk","title":"Step 3. Read images from the disk.","text":"

To pass an image to the augmentation pipeline, you need to read it from the disk. The pipeline expects to receive an image in the form of a NumPy array. If it is a color image, it should have three channels in the following order: Red, Green, Blue (so a regular RGB image).

To read images from the disk, you can use OpenCV - a popular library for image processing. It supports a lot of input formats and is installed along with Albumentations since Albumentations utilizes that library under the hood for a lot of augmentations.

To import OpenCV

Python
import cv2\n

To read an image with OpenCV

Python

image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
Note the usage of cv2.cvtColor. For historical reasons, OpenCV reads an image in BGR format (so color channels of the image have the following order: Blue, Green, Red). Albumentations uses the most common and popular RGB image format. So when using OpenCV, we need to convert the image format to RGB explicitly.

Besides OpenCV, you can use other image processing libraries.

"},{"location":"getting_started/image_augmentation/#pillow","title":"Pillow","text":"

Pillow is a popular Python image processing library.

  • Install Pillow
Bash
    pip install pillow\n
  • Import Pillow and NumPy (we need NumPy to convert a Pillow image to a NumPy array. NumPy is already installed along with Albumentations).
Python
from PIL import Image\nimport numpy as np\n
  • Read an image with Pillow and convert it to a NumPy array. Python
    pillow_image = Image.open(\"image.jpg\")\nimage = np.array(pillow_image)\n
"},{"location":"getting_started/image_augmentation/#step-4-pass-images-to-the-augmentation-pipeline-and-receive-augmented-images","title":"Step 4. Pass images to the augmentation pipeline and receive augmented images.","text":"

To pass an image to the augmentation pipeline you need to call the transform function created by a call to A.Compose at Step 2. In the image argument to that function, you need to pass an image that you want to augment.

Python
transformed = transform(image=image)\n

transform will return a dictionary with a single key image. Value at that key will contain an augmented image.

Python
transformed_image = transformed[\"image\"]\n

To augment the next image, you need to call transform again and pass a new image as the image argument:

Python
another_transformed_image = transform(image=another_image)[\"image\"]\n

Each augmentation will change the input image with the probability set by the parameter p. Also, many augmentations have parameters that control the magnitude of changes that will be applied to an image. For example, A.RandomBrightnessContrast has two parameters: brightness_limit that controls the magnitude of adjusting brightness and contrast_limit that controls the magnitude of adjusting contrast. The bigger the value, the more the augmentation will change an image. During augmentation, a magnitude of the transformation is sampled from a uniform distribution limited by brightness_limit and contrast_limit. That means that if you make multiple calls to transform with the same input image, you will get a different output image each time.

Python
transform = A.Compose([\n    A.RandomBrightnessContrast(brightness_limit=1, contrast_limit=1, p=1.0),\n])\ntransformed_image_1 = transform(image=image)['image']\ntransformed_image_2 = transform(image=image)['image']\ntransformed_image_3 = transform(image=image)['image']\n

"},{"location":"getting_started/image_augmentation/#examples","title":"Examples","text":"
  • Defining a simple augmentation pipeline for image augmentation
  • Working with non-8-bit images
  • Weather augmentations in Albumentations
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/installation/","title":"Installation","text":"

Albumentations requires Python 3.8 or higher.

"},{"location":"getting_started/installation/#install-the-latest-stable-version-from-pypi","title":"Install the latest stable version from PyPI","text":"Bash
pip install -U albumentations\n
"},{"location":"getting_started/installation/#install-the-latest-version-from-the-master-branch-on-github","title":"Install the latest version from the master branch on GitHub","text":"Bash
pip install -U git+https://github.com/albumentations-team/albumentations\n
"},{"location":"getting_started/installation/#note-on-opencv-dependencies","title":"Note on OpenCV dependencies","text":"

By default, pip downloads a wheel distribution of Albumentations. This distribution has opencv-python-headless as its dependency.

If you already have some OpenCV distribution (such as opencv-python-headless, opencv-python, opencv-contrib-python or opencv-contrib-python-headless) installed in your Python environment, you can force Albumentations to use it by providing the --no-binary qudida,albumentations argument to pip, e.g.

Bash
pip install -U albumentations\n

pip will use the following logic to determine the required OpenCV distribution:

  1. If your Python environment already contains opencv-python, opencv-contrib-python, opencv-contrib-python-headless or opencv-python-headless pip will use it.
  2. If your Python environment doesn't contain any OpenCV distribution from step 1, pip will download opencv-python-headless.
"},{"location":"getting_started/installation/#install-the-latest-stable-version-from-conda-forge","title":"Install the latest stable version from conda-forge","text":"

If you are using Anaconda or Miniconda you can install Albumentations from conda-forge:

Bash
conda install -c conda-forge albumentations\n
"},{"location":"getting_started/keypoints_augmentation/","title":"Keypoints augmentation","text":"

Computer vision tasks such as human pose estimation, face detection, and emotion recognition usually work with keypoints on the image.

In the case of pose estimation, keypoints mark human joints such as shoulder, elbow, wrist, knee, etc.

Keypoints annotations along with visualized edges between keypoints. Images are from the COCO dataset.

In the case of face detection, keypoints mark important areas of the face such as eyes, nose, corners of the mouth, etc.

Facial keypoints. Source: the \"Facial Keypoints Detection\" competition on Kaggle.

To define a keypoint, you usually need two values, x and y coordinates of the keypoint. Coordinates of the keypoint are calculated with respect to the top-left corner of the image which has (x, y) coordinates (0, 0). Often keypoints have associated labels such as right_elbow, left_wrist, etc.

An example image with five keypoints from the COCO dataset

Some classical computer vision algorithms, such as SIFT, may use four values to describe a keypoint. In addition to the x and y coordinates, there are keypoint scale and keypoint angle. Albumentations support those values as well.

A keypoint may also has associated scale and angle values

Keypoint angles are counter-clockwise. For example, in the following image, the angle value is 65\u00b0. You can read more about angle of rotation in the Wikipedia article.

"},{"location":"getting_started/keypoints_augmentation/#supported-formats-for-keypoints-coordinates","title":"Supported formats for keypoints' coordinates.","text":"
  • xy. A keypoint is defined by x and y coordinates in pixels.

  • yx. A keypoint is defined by y and x coordinates in pixels.

  • xya. A keypoint is defined by x and y coordinates in pixels and the angle.

  • xys. A keypoint is defined by x and y coordinates in pixels, and the scale.

  • xyas. A keypoint is defined by x and y coordinates in pixels, the angle, and the scale.

  • xysa. A keypoint is defined by x and y coordinates in pixels, the scale, and the angle.

"},{"location":"getting_started/keypoints_augmentation/#augmenting-keypoints","title":"Augmenting keypoints","text":"

The process of augmenting keypoints looks very similar to the bounding boxes augmentation. It consists of 4 steps.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and keypoints from the disk.
  4. You pass an image and keypoints to the augmentation pipeline and receive augmented images and keypoints.

Note

Some transforms in Albumentation don't support keypoints. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment keypoints.

"},{"location":"getting_started/keypoints_augmentation/#step-1-import-the-required-libraries","title":"Step 1. Import the required libraries.","text":"Python
import albumentations as A\nimport cv2\n
"},{"location":"getting_started/keypoints_augmentation/#step-2-define-an-augmentation-pipeline","title":"Step 2. Define an augmentation pipeline.","text":"

Here an example of a minimal declaration of an augmentation pipeline that works with keypoints.

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy'))\n

Note that just like with bounding boxes, Compose has an additional parameter that defines the format for keypoints' coordinates. In the case of keypoints, it is called keypoint_params. Here we pass an instance of A.KeypointParams that says that xy coordinates format should be used.

Besides format, A.KeypointParams supports a few more settings.

Here is an example of Compose that shows all available settings with A.KeypointParams

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels'], remove_invisible=True, angle_in_degrees=True))\n
"},{"location":"getting_started/keypoints_augmentation/#label_fields","title":"label_fields","text":"

In some computer vision tasks, keypoints have not only coordinates but associated labels as well. For example, in pose estimation, each keypoint has a label such as elbow, knee or wrist. You need to pass those labels in a separate argument (or arguments, because you can use multiple fields) to the transform function that will augment keypoints. label_fields defines names of those fields. Step 4 describes how you need to use the transform function.

"},{"location":"getting_started/keypoints_augmentation/#remove_invisible","title":"remove_invisible","text":"

After the augmentation, some keypoints may become invisible because they will be located outside of the augmented image's visible area. For example, if you crop a part of the image, all the keypoints outside of the cropped area will become invisible. If remove_invisible is set to True, Albumentations won't return invisible keypoints. remove_invisible is set to True by default, so if you don't pass that argument, Albumentations won't return invisible keypoints.

"},{"location":"getting_started/keypoints_augmentation/#angle_in_degrees","title":"angle_in_degrees","text":"

If angle_in_degrees is set to True (this is the default value), then Albumentations expects that the angle value in formats xya, xyas, and xysa is defined in angles. If angle_in_degrees is set to False, Albumentations expects that the angle value is specified in radians.

This setting doesn't affect xy and yx formats, because those formats don't use angles.

"},{"location":"getting_started/keypoints_augmentation/#3-read-images-and-keypoints-from-the-disk","title":"3. Read images and keypoints from the disk.","text":"

Read an image from the disk.

Python

image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
Keypoints can be stored on the disk in different serialization formats: JSON, XML, YAML, CSV, etc. So the code to read keypoints depends on the actual format of data on the disk.

After you read the data from the disk, you need to prepare keypoints for Albumentations.

Albumentations expects that keypoint will be represented as a list of lists. Each list contains information about a single keypoint. A definition of keypoint should have two to four elements depending on the selected format of keypoints. The first two elements are x and y coordinates of a keypoint in pixels (or y and x coordinates in the yx format). The third and fourth elements may be the angle and the scale of keypoint if you select a format that uses those values.

"},{"location":"getting_started/keypoints_augmentation/#step-4-pass-an-image-and-keypoints-to-the-augmentation-pipeline-and-receive-augmented-images-and-boxes","title":"Step 4. Pass an image and keypoints to the augmentation pipeline and receive augmented images and boxes.","text":"

Let's say you have an example image with five keypoints.

A list with those five keypoints' coordinates in the xy format will look the following:

Python
keypoints = [\n    (264, 203),\n    (86, 88),\n    (254, 160),\n    (193, 103),\n    (65, 341),\n]\n

Then you pass those keypoints to the transform function along with the image and receive the augmented versions of image and keypoints.

Python
transformed = transform(image=image, keypoints=keypoints)\ntransformed_image = transformed['image']\ntransformed_keypoints = transformed['keypoints']\n

The augmented image with augmented keypoints

If you set remove_invisible to False in keypoint_params, then Albumentations will return all keypoints, even if they lie outside the visible area. In the example image below, you can see that the keypoint for the right hip is located outside the image, but Albumentations still returned it. The area outside the image is highlighted in yellow.

When remove_invisible is set to False Albumentations will return all keypoints, even those located outside the image

If keypoints have associated class labels, you need to create a list that contains those labels:

Python
class_labels = [\n    'left_elbow',\n    'right_elbow',\n    'left_wrist',\n    'right_wrist',\n    'right_hip',\n]\n

Also, you need to declare the name of the argument to transform that will contain those labels. For declaration, you need to use the label_fields parameters of A.KeypointParams.

For example, we could use the class_labels name for the argument with labels.

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels']))\n

Next, you pass both keypoints' coordinates and class labels to transform.

Python
transformed = transform(image=image, keypoints=keypoints, class_labels=class_labels)\ntransformed_image = transformed['image']\ntransformed_keypoints = transformed['keypoints']\ntransformed_class_labels = transformed['class_labels']\n

Note that label_fields expects a list, so you can set multiple fields that contain labels for your keypoints. So if you declare Compose like

Python
transform = A.Compose([\n    A.RandomCrop(width=330, height=330),\n    A.RandomBrightnessContrast(p=0.2),\n], keypoint_params=A.KeypointParams(format='xy', label_fields=['class_labels', 'class_sides']))\n

you can use those multiple arguments to pass info about class labels, like

Python
class_labels = [\n    'left_elbow',\n    'right_elbow',\n    'left_wrist',\n    'right_wrist',\n    'right_hip',\n]\n\nclass_sides = ['left', 'right', 'left', 'right', 'right']\n\ntransformed = transform(image=image, keypoints=keypoints, class_labels=class_labels, class_sides=class_sides)\ntransformed_class_sides = transformed['class_sides']\ntransformed_class_labels = transformed['class_labels']\ntransformed_keypoints = transformed['keypoints']\ntransformed_image = transformed['image']\n

Example input and output data for keypoints augmentation with two separate arguments for class labels

Note

Some augmentations may affect class labels and make them incorrect. For example, the HorizontalFlip augmentation mirrors the input image. When you apply that augmentation to keypoints that mark the side of body parts (left or right), those keypoints will point to the wrong side (since left on the mirrored image becomes right). So when you are creating an augmentation pipeline look carefully which augmentations could be applied to the input data.

HorizontalFlip may make keypoints' labels incorrect

"},{"location":"getting_started/keypoints_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations to augment keypoints
"},{"location":"getting_started/mask_augmentation/","title":"Mask augmentation for segmentation","text":"

For instance and semantic segmentation tasks, you need to augment both the input image and one or more output masks.

Albumentations ensures that the input image and the output mask will receive the same set of augmentations with the same parameters.

The process of augmenting images and masks looks very similar to the regular image-only augmentation.

  1. You import the required libraries.
  2. You define an augmentation pipeline.
  3. You read images and masks from the disk.
  4. You pass an image and one or more masks to the augmentation pipeline and receive augmented images and masks.
"},{"location":"getting_started/mask_augmentation/#steps-1-and-2-import-the-required-libraries-and-define-an-augmentation-pipeline","title":"Steps 1 and 2. Import the required libraries and define an augmentation pipeline.","text":"

Image augmentation for classification described Steps 1 and 2 in great detail. These are the same steps for the simultaneous augmentation of images and masks.

Python
import albumentations as A\nimport cv2\n\ntransform = A.Compose([\n    A.RandomCrop(width=256, height=256),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n
"},{"location":"getting_started/mask_augmentation/#step-3-read-images-and-masks-from-the-disk","title":"Step 3. Read images and masks from the disk.","text":"
  • Reading an image
Python
image = cv2.imread(\"/path/to/image.jpg\")\nimage = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n
  • For semantic segmentation, you usually read one mask per image. Albumentations expects the mask to be a NumPy array. The height and width of the mask should have the same values as the height and width of the image.
Python
mask = cv2.imread(\"/path/to/mask.png\")\n
  • For instance segmentation, you sometimes need to read multiple masks per image. Then you create a list that contains all the masks.
Python
mask_1 = cv2.imread(\"/path/to/mask_1.png\")\nmask_2 = cv2.imread(\"/path/to/mask_2.png\")\nmask_3 = cv2.imread(\"/path/to/mask_3.png\")\nmasks = [mask_1, mask_2, mask_3]\n

Some datasets use other formats to store masks. For example, they can use Run-Length Encoding or Polygon coordinates. In that case, you need to convert a mask to a NumPy before augmenting it with Albumentations. Often dataset authors provide special libraries and tools to simplify the conversion.

"},{"location":"getting_started/mask_augmentation/#step-4-pass-image-and-masks-to-the-augmentation-pipeline-and-receive-augmented-images-and-masks","title":"Step 4. Pass image and masks to the augmentation pipeline and receive augmented images and masks.","text":"

If the image has one associated mask, you need to call transform with two arguments: image and mask. In image you should pass the input image, in mask you should pass the output mask. transform will return a dictionary with two keys: image will contain the augmented image, and mask will contain the augmented mask.

Python
transformed = transform(image=image, mask=mask)\ntransformed_image = transformed['image']\ntransformed_mask = transformed['mask']\n

An image and a mask before and after augmentation. Inria Aerial Image Labeling dataset contains aerial photos as well as their segmentation masks. Each pixel of the mask is marked as 1 if the pixel belongs to the class building and 0 otherwise.

If the image has multiple associated masks, you should use the masks argument instead of mask. In masks you should pass a list of masks.

Python
transformed = transform(image=image, masks=masks)\ntransformed_image = transformed['image']\ntransformed_masks = transformed['masks']\n
"},{"location":"getting_started/mask_augmentation/#examples","title":"Examples","text":"
  • Using Albumentations for a semantic segmentation task
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/setting_probabilities/","title":"Setting probabilities for transforms in an augmentation pipeline","text":"

Each augmentation in Albumentations has a parameter named p that sets the probability of applying that augmentation to input data.

The following augmentations have the default value of p set 1 (which means that by default they will be applied to each instance of input data): Compose, ReplayCompose, CenterCrop, Crop, CropNonEmptyMaskIfExists, FromFloat, CenterCrop, Crop, CropNonEmptyMaskIfExists, FromFloat, IAACropAndPad, Lambda, LongestMaxSize, Normalize, PadIfNeeded, RandomCrop, RandomCropNearBBox, RandomResizedCrop, RandomSizedBBoxSafeCrop, RandomSizedCrop, Resize, SmallestMaxSize, ToFloat.

All other augmentations have the default value of p set 0.5, which means that by default, they will be applied to 50% of instances of input data.

Let's take a look at the example:

Python
import albumentations as A\nimport cv2\n\np1 = 0.95\np2 = 0.85\np3 = 0.75\n\n\ntransform = A.Compose([\n    A.RandomRotate90(p=p2),\n    A.OneOf([\n        A.IAAAdditiveGaussianNoise(p=0.9),\n        A.GaussNoise(p=0.6),\n    ], p=p3)\n], p=p1)\n\nimage = cv2.imread('some/image.jpg')\nimage = cv2.cvtColor(cv2.COLOR_BGR2RGB)\n\ntransformed = transform(image=image)\ntransformed_image = transformed['image']\n

We declare an augmentation pipeline. In this pipeline, we use three placeholder values to set probabilities: p1, p2, and p3. Let's take a closer look at them.

"},{"location":"getting_started/setting_probabilities/#p1","title":"p1","text":"

p1 sets the probability that the augmentation pipeline will apply augmentations at all.

If p1 is set to 0, then augmentations inside Compose will never be applied to the input image, so the augmentation pipeline will always return the input image unchanged.

If p1 is set to 1, then all augmentations inside Compose will have a chance to be applied. The example above contains two augmentations inside Compose: RandomRotate90 and the OneOf block with two child augmentations (more on their probabilities later). Any value of p1 between 0 and 1 means that augmentations inside Compose could be applied with the probability between 0 and 100%.

If p1 equals to 1 or p1 is less than 1, but the random generator decides to apply augmentations inside Compose probabilities p2 and p3 come into play.

"},{"location":"getting_started/setting_probabilities/#p2","title":"p2","text":"

Each augmentation inside Compose has a probability of being applied. p2 sets the probability of applying RandomRotate90. In the example above, p2 equals 0.85, so RandomRotate90 has an 85% chance to be applied to the input image.

"},{"location":"getting_started/setting_probabilities/#p3","title":"p3","text":"

p3 sets the probability of applying the OneOf block. If the random generator decided to apply RandomRotate90 at the previous step, then OneOf will receive data augmented by it. If the random generator decided not to apply RandomRotate90 then OneOf will receive the input data (that was passed to Compose) since RandomRotate90 is skipped.

The OneOfblock applies one of the augmentations inside it. That means that if the random generator chooses to apply OneOf then one child augmentation from it will be applied to the input data.

To decide which augmentation within the OneOf block is used, Albumentations uses the following rule:

The OneOf block normalizes the probabilities of all augmentations inside it, so their probabilities sum up to 1. Next, OneOf chooses one of the augmentations inside it with a chance defined by its normalized probability and applies it to the input data. In the example above IAAAdditiveGaussianNoise has probability 0.9 and GaussNoise probability 0.6. After normalization, they become 0.6 and 0.4. Which means that OneOf will decide that it should use IAAAdditiveGaussianNoise with probability 0.6 and GaussNoise otherwise.

"},{"location":"getting_started/setting_probabilities/#example-calculations","title":"Example calculations","text":"

Thus, each augmentation in the example above will be applied with the probability:

  • RandomRotate90: p1 * p2
  • IAAAdditiveGaussianNoise: p1 * p3 * (0.9 / (0.9 + 0.6))
  • GaussianNoise: p1 * p3 * (0.6 / (0.9 + 0.6))
"},{"location":"getting_started/simultaneous_augmentation/","title":"Simultaneous augmentation of multiple targets: masks, bounding boxes, keypoints","text":"

Albumentations can apply the same set of transformations to the input images and all the targets that are passed to transform: masks, bounding boxes, and keypoints.

Please refer to articles Image augmentation for classification, Mask augmentation for segmentation, Bounding boxes augmentation for object detection, and Keypoints augmentation for the detailed description of each data type.

Note

Some transforms in Albumentation don't support bounding boxes or keypoints. If you try to use them you will get an exception. Please refer to this article to check whether a transform can augment bounding boxes and keypoints.

Below is an example, how you can simultaneously augment the input image, mask, bounding boxes with their labels, and keypoints with their labels. Note that the only required argument to transform is image; all other arguments are optional, and you can combine them in any way.

"},{"location":"getting_started/simultaneous_augmentation/#step-1-define-compose-with-parameters-that-specify-formats-for-bounding-boxes-and-keypoints","title":"Step 1. Define Compose with parameters that specify formats for bounding boxes and keypoints.","text":"Python
transform = A.Compose(\n  [A.RandomCrop(width=330, height=330), A.RandomBrightnessContrast(p=0.2)],\n  bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"bbox_classes\"]),\n  keypoint_params=A.KeypointParams(format=\"xy\", label_fields=[\"keypoints_classes\"]),\n)\n
"},{"location":"getting_started/simultaneous_augmentation/#step-2-load-all-required-data-from-the-disk","title":"Step 2. Load all required data from the disk","text":"

Please refer to articles Image augmentation for classification, Mask augmentation for segmentation, Bounding boxes augmentation for object detection, and Keypoints augmentation for more information about loading the input data.

For example, here is an image from the COCO dataset. that has one associated mask, one bounding box with the class label person, and five keypoints that define body parts.

An example image with mask, bounding boxes and keypoints

"},{"location":"getting_started/simultaneous_augmentation/#step-3-pass-all-targets-to-transform-and-receive-their-augmented-versions","title":"Step 3. Pass all targets to transform and receive their augmented versions","text":"Python
transformed = transform(\n  image=img,\n  mask=mask,\n  bboxes=bboxes,\n  bbox_classes=bbox_classes,\n  keypoints=keypoints,\n  keypoints_classes=keypoints_classes,\n)\ntransformed_image = transformed[\"image\"]\ntransformed_mask = transformed[\"mask\"]\ntransformed_bboxes = transformed[\"bboxes\"]\ntransformed_bbox_classes = transformed[\"bbox_classes\"]\ntransformed_keypoints = transformed[\"keypoints\"]\ntransformed_keypoints_classes = transformed[\"keypoints_classes\"]\n

The augmented version of the image and its targets

"},{"location":"getting_started/simultaneous_augmentation/#examples","title":"Examples","text":"
  • Showcase. Cool augmentation examples on diverse set of images from various real-world tasks.
"},{"location":"getting_started/transforms_and_targets/","title":"A list of transforms and their supported targets","text":"

We can split all transforms into two groups: pixel-level transforms, and spatial-level transforms. Pixel-level transforms will change just an input image and will leave any additional targets such as masks, bounding boxes, and keypoints unchanged. Spatial-level transforms will simultaneously change both an input image as well as additional targets such as masks, bounding boxes, and keypoints. For the additional information, please refer to this section of \"Why you need a dedicated library for image augmentation\".

"},{"location":"getting_started/transforms_and_targets/#pixel-level-transforms","title":"Pixel-level transforms","text":"

Here is a list of all available pixel-level transforms. You can apply a pixel-level transform to any target, and under the hood, the transform will change only the input image and return any other input targets such as masks, bounding boxes, or keypoints unchanged.

  • AdditiveNoise
  • AdvancedBlur
  • AutoContrast
  • Blur
  • CLAHE
  • ChannelDropout
  • ChannelShuffle
  • ChromaticAberration
  • ColorJitter
  • Defocus
  • Downscale
  • Emboss
  • Equalize
  • FDA
  • FancyPCA
  • FromFloat
  • GaussNoise
  • GaussianBlur
  • GlassBlur
  • HistogramMatching
  • HueSaturationValue
  • ISONoise
  • Illumination
  • ImageCompression
  • InvertImg
  • MedianBlur
  • MotionBlur
  • MultiplicativeNoise
  • Normalize
  • PixelDistributionAdaptation
  • PlanckianJitter
  • PlasmaBrightnessContrast
  • PlasmaShadow
  • Posterize
  • RGBShift
  • RandomBrightnessContrast
  • RandomFog
  • RandomGamma
  • RandomGravel
  • RandomRain
  • RandomShadow
  • RandomSnow
  • RandomSunFlare
  • RandomToneCurve
  • RingingOvershoot
  • SaltAndPepper
  • Sharpen
  • ShotNoise
  • Solarize
  • Spatter
  • Superpixels
  • TemplateTransform
  • TextImage
  • ToFloat
  • ToGray
  • ToRGB
  • ToSepia
  • UnsharpMask
  • ZoomBlur
"},{"location":"getting_started/transforms_and_targets/#spatial-level-transforms","title":"Spatial-level transforms","text":"

Here is a table with spatial-level transforms and targets they support. If you try to apply a spatial-level transform to an unsupported target, Albumentations will raise an error.

Transform Image Mask BBoxes Keypoints Volume Mask3D Affine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 AtLeastOneBBoxRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 BBoxSafeRandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CenterCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CoarseDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Crop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropAndPad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 CropNonEmptyMaskIfExists \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 D4 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ElasticTransform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Erasing \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 FrequencyMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 GridElasticDeform \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 HorizontalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Lambda \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 LongestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 MaskDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Morphological \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 NoOp \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OpticalDistortion \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 OverlayElements \u2713 \u2713 Pad \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PadIfNeeded \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Perspective \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PiecewiseAffine \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 PixelDropout \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropFromBorders \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomCropNearBBox \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomGridShuffle \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomResizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomRotate90 \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomScale \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedBBoxSafeCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 RandomSizedCrop \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Resize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Rotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SafeRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ShiftScaleRotate \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 SmallestMaxSize \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 ThinPlateSpline \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 TimeReverse \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 Transpose \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 VerticalFlip \u2713 \u2713 \u2713 \u2713 \u2713 \u2713 XYMasking \u2713 \u2713 \u2713 \u2713 \u2713 \u2713"},{"location":"getting_started/video_augmentation/","title":"Working with Video Data in Albumentations","text":""},{"location":"getting_started/video_augmentation/#overview","title":"Overview","text":"

While Albumentations is primarily known for image augmentation, it can effectively process video data by treating it as a sequence of frames. When you pass a video as a numpy array, Albumentations will apply the same transform with identical parameters to each frame, ensuring temporal consistency.

"},{"location":"getting_started/video_augmentation/#data-format","title":"Data Format","text":""},{"location":"getting_started/video_augmentation/#video-frames","title":"Video Frames","text":"

Albumentations accepts video data as numpy arrays in the following formats: - (N, H, W) - Grayscale video (N frames) - (N, H, W, C) - Color video (N frames)

Where: - N = Number of frames - H = Height - W = Width - C = Channels (e.g., 3 for RGB)

"},{"location":"getting_started/video_augmentation/#video-masks","title":"Video Masks","text":"

For video segmentation tasks, masks should match the frame dimensions: - (N, H, W) - Binary or single-class masks - (N, H, W, C) - Multi-class masks

"},{"location":"getting_started/video_augmentation/#basic-usage","title":"Basic Usage","text":"Python
import albumentations as A\nimport numpy as np\n
"},{"location":"getting_started/video_augmentation/#create-transform-pipeline","title":"Create transform pipeline","text":"Python
transform = A.Compose([\n    A.RandomCrop(height=224, width=224),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n], seed=42)\n
"},{"location":"getting_started/video_augmentation/#example-video-data","title":"Example video data","text":"Python
video = np.random.rand(32, 256, 256, 3) # 32 RGB frames\nmasks = np.zeros((32, 256, 256)) # 32 binary masks\n
"},{"location":"getting_started/video_augmentation/#apply-transform","title":"Apply transform","text":"Python
augmented_video = transform(images=video, masks=masks)\n
"},{"location":"getting_started/video_augmentation/#apply-transforms-same-parameters-for-all-frames","title":"Apply transforms - same parameters for all frames","text":"Python
transformed = transform(images=video, mask=masks)\ntransformed_video = transformed['image']\ntransformed_masks = transformed['mask']\n
"},{"location":"getting_started/video_augmentation/#key-features","title":"Key Features","text":"
  1. Temporal Consistency: The same transform with identical parameters is applied to all frames, preserving temporal consistency.

  2. Memory Efficiency: Frames are processed as a batch, avoiding repeated parameter generation.

  3. Compatible with All Transforms: Works with any Albumentations transform that supports the image target.

"},{"location":"getting_started/video_augmentation/#example-pipeline-for-video-processing","title":"Example Pipeline for Video Processing","text":"Python
def create_video_pipeline(\n    crop_size=(224, 224),\n    p_spatial=0.5,\n    p_color=0.3\n    ):\n    return A.Compose([\n        # Spatial transforms - same crop/flip for all frames\n        A.RandomCrop(\n            height=crop_size[0],\n            width=crop_size[1],\n            p=1.0\n        ),\n        A.HorizontalFlip(p=p_spatial),\n        # Color transforms - same adjustment for all frames\n        A.ColorJitter(\n            brightness=0.2,\n            contrast=0.2,\n            saturation=0.2,\n            hue=0.1,\n            p=p_color\n        ),\n        # Noise/blur - same pattern for all frames\n        A.GaussianBlur(p=0.3),\n    ])\n
"},{"location":"getting_started/video_augmentation/#best-practices","title":"Best Practices","text":"
  1. Performance Optimization:
  2. Place cropping operations first to reduce computation
  3. Consider frame rate and whether all frames need processing
"},{"location":"getting_started/video_augmentation/#next-steps","title":"Next Steps","text":"
  • Learn about Volumetric Data (3D) for volumetric data
"},{"location":"getting_started/volumetric_augmentation/","title":"Introduction to 3D Medical Image Augmentation","text":""},{"location":"getting_started/volumetric_augmentation/#overview","title":"Overview","text":"

While primarily used for medical imaging (CT scans, MRI), Albumentations' 3D transforms can be applied to various volumetric data types

"},{"location":"getting_started/volumetric_augmentation/#medical-imaging","title":"Medical Imaging","text":"
  • CT and MRI scans
  • Ultrasound volumes
  • PET scans
  • Multi-modal medical imaging
"},{"location":"getting_started/volumetric_augmentation/#scientific-data","title":"Scientific Data","text":"
  • Microscopy z-stacks
  • Cryo-EM volumes
  • Geological seismic data
  • Weather radar volumes
"},{"location":"getting_started/volumetric_augmentation/#industrial-applications","title":"Industrial Applications","text":"
  • 3D NDT (Non-Destructive Testing) scans
  • Industrial CT for quality control
  • Material analysis volumes
  • 3D ultrasonic testing data
"},{"location":"getting_started/volumetric_augmentation/#computer-vision","title":"Computer Vision","text":"
  • Depth camera sequences
  • LiDAR point cloud voxelizations
  • Multi-view stereo reconstructions
"},{"location":"getting_started/volumetric_augmentation/#data-format","title":"Data Format","text":""},{"location":"getting_started/volumetric_augmentation/#volumes","title":"Volumes","text":"

Albumentations expects 3D volumes as numpy arrays in the following formats: - (D, H, W) - Single-channel volumes (e.g., CT scans) - (D, H, W, C) - Multi-channel volumes (e.g., multi-modal MRI)

Where: - D = Depth (number of slices) - H = Height - W = Width - C = Channels (optional)

"},{"location":"getting_started/volumetric_augmentation/#3d-masks","title":"3D Masks","text":"

Segmentation masks should match the volume dimensions: - (D, H, W) - Binary or single-class masks - (D, H, W, C) - Multi-class masks

"},{"location":"getting_started/volumetric_augmentation/#basic-usage","title":"Basic Usage","text":"Python
import albumentations as A\nimport numpy as np\n
"},{"location":"getting_started/volumetric_augmentation/#create-a-basic-3d-augmentation-pipeline","title":"Create a basic 3D augmentation pipeline","text":"Python
transform = A.Compose([\n    # Crop volume to a fixed size for memory efficiency\n    A.RandomCrop3D(size=(64, 128, 128), p=1.0),    \n    # Randomly remove cubic regions to simulate occlusions\n    A.CoarseDropout3D(\n        num_holes_range=(2, 6),\n        hole_depth_range=(0.1, 0.3),\n        hole_height_range=(0.1, 0.3),\n        hole_width_range=(0.1, 0.3),\n        p=0.5\n    ),    \n])\n
"},{"location":"getting_started/volumetric_augmentation/#apply-to-volume-and-mask","title":"Apply to volume and mask","text":"Python
volume = np.random.rand(96, 256, 256) # Your 3D medical volume\nmask = np.zeros((96, 256, 256)) # Your 3D segmentation mask\ntransformed = transform(volume=volume, mask3d=mask)\ntransformed_volume = transformed['volume']\ntransformed_mask = transformed['mask3d']\n
"},{"location":"getting_started/volumetric_augmentation/#available-3d-transforms","title":"Available 3D Transforms","text":"

Here are some examples of available 3D transforms:

  • CenterCrop3D - Crop the center part of a 3D volume
  • RandomCrop3D - Randomly crop a part of a 3D volume
  • Pad3D - Pad a 3D volume
  • PadIfNeeded3D - Pad if volume size is less than desired size
  • CoarseDropout3D - Random dropout of 3D cubic regions
  • CubicSymmetry - Apply random cubic symmetry transformations

For a complete and up-to-date list of all available 3D transforms, please see our API Reference.

"},{"location":"getting_started/volumetric_augmentation/#combining-2d-and-3d-transforms","title":"Combining 2D and 3D Transforms","text":"

You can combine 2D and 3D transforms in the same pipeline. 2D transforms will be applied slice-by-slice in the XY plane:

Python
transform = A.Compose([\n    # 3D transforms\n    A.RandomCrop3D(size=(64, 128, 128)),\n    # 2D transforms (applied to each XY slice)\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n])\n\ntransformed = transform(volume=volume, mask3d=mask)\ntransformed_volume = transformed['volume']\ntransformed_mask = transformed['mask3d']\n
"},{"location":"getting_started/volumetric_augmentation/#best-practices","title":"Best Practices","text":"
  1. Memory Management: 3D volumes can be large. Consider using smaller crop sizes or processing in patches.
  2. Place cropping operations at the beginning of your pipeline for better performance
  3. Example: A 256x256x256 volume cropped to 64x64x64 will process subsequent transforms ~64x faster
"},{"location":"getting_started/volumetric_augmentation/#efficient-pipeline-cropping-first","title":"Efficient pipeline - cropping first","text":"Python
efficient_transform = A.Compose([\nA.RandomCrop3D(size=(64, 64, 64)), # Do this first!\nA.CoarseDropout3D(...),\nA.RandomBrightnessContrast(...)\n])\n
"},{"location":"getting_started/volumetric_augmentation/#less-efficient-pipeline-processing-full-volume-unnecessarily","title":"Less efficient pipeline - processing full volume unnecessarily","text":"Python
inefficient_transform = A.Compose([\nA.CoarseDropout3D(...), # Processing full volume\nA.RandomBrightnessContrast(...), # Processing full volume\nA.RandomCrop3D(size=(64, 64, 64)) # Cropping at the end\n])\n
  1. Avoid Interpolation Artifacts: For highest quality augmentation, prefer transforms that only rearrange existing voxels without interpolation:

a) Available Artifact-Free Transforms: - HorizontalFlip, VerticalFlip - Mirror images across X or Y axes - RandomRotate90 - Rotate by 90 degrees in XY plane - D4 - All possible combinations of flips and 90-degree rotations in XY plane (8 variants) - CubicSymmetry - 3D extension of D4, includes all 48 possible cube symmetries

These transforms maintain perfect image quality because they only move existing voxels to new positions without creating new values through interpolation.

b) Benefits of Artifact-Free Transforms:\n- Preserve original voxel values exactly\n- Maintain spatial relationships between tissues\n- No blurring or information loss\n- Faster computation (no interpolation needed)\n
"},{"location":"getting_started/volumetric_augmentation/#example-pipeline","title":"Example Pipeline","text":"

Here's a complete example of a medical image augmentation pipeline:

Python
import albumentations as A\nimport numpy as np\n\ndef create_3d_pipeline(\n    crop_size=(64, 128, 128),\n    p_spatial=0.5,\n    p_intensity=0.3\n    ):\n    return A.Compose([\n        # Spatial transforms\n        A.RandomCrop3D(\n            size=crop_size,\n            p=1.0\n        ),\n        A.CubicSymmetry(p=p_spatial),\n        # Intensity transforms\n        A.CoarseDropout3D(\n            num_holes_range=(2, 5),\n            hole_depth_range=(0.1, 0.2),\n            hole_height_range=(0.1, 0.2),\n            hole_width_range=(0.1, 0.2),\n            p=p_intensity\n        ),\n    ])\n
"},{"location":"getting_started/volumetric_augmentation/#usage","title":"Usage","text":"Python
transform = create_3d_pipeline()\nvolume = np.random.rand(96, 256, 256)\nmask = np.zeros((96, 256, 256))\ntransformed = transform(volume=volume, mask3d=mask)\n
"},{"location":"getting_started/volumetric_augmentation/#next-steps","title":"Next Steps","text":"
  • Learn about Video Augmentation for sequential data
"},{"location":"integrations/","title":"Integrations","text":"

Here are some examples of how to use Albumentations with different deep learning frameworks and tools:

  • HuggingFace
  • FiftyOne
  • Roboflow
"},{"location":"integrations/fiftyone/","title":"FiftyOne integration","text":""},{"location":"integrations/fiftyone/#introduction","title":"Introduction","text":"

FiftyOne is an open-source visualization and analysis tool for machine learning datasets, particularly useful in computer vision projects. It facilitates detailed dataset examination and the fine-tuning of model performance.

Albumentations could be used in FiftyOne via the FiftyOne Plugin.

With the FiftyOne Albumentations plugin, you can transform any and all labels of type Detections, Keypoints, Segmentation, and Heatmap, or just the images themselves.

Info

This tutorial is almost entirely based on the FiftyOne Documentation and serves as an overview of the functionality of the FiftyOne Albumentations plugin.

For more up to date information check the original source.

This integration guide will focus on the setup process and the functionality of the plugin.

For a tutorial on how to curate your augmentations, check out the Data Augmentation Tutorial as FiftyOne Documentation.

"},{"location":"integrations/fiftyone/#overview","title":"Overview","text":"

Albumentations supports 80+ transforms spanning pixel-level, geometric transformations, and more.

As of April 29, 2024 FiftyOne supports:

  • AdvancedBlur
  • GridDropout
  • MaskDropout
  • PiecewiseAffine
  • RandomGravel
  • RandomGridShuffle
  • RandomShadow
  • RandomSunFlare
  • Rotate
"},{"location":"integrations/fiftyone/#functionality","title":"Functionality","text":"

The FiftyOne Albumentations plugin provides the following functionality:

  • Apply Albumentations transformations to your dataset, your current view, or selected samples
  • Visualize the effects of these transformations directly within the FiftyOne App
  • View samples generated by the last applied transformation
  • Save augmented samples to the dataset
  • Get info about the last applied transformation
  • Save transformation pipelines to the dataset for reproducibility
"},{"location":"integrations/fiftyone/#setup","title":"Setup","text":"

Make sure you have FiftyOne and Albumentations installed:

Bash
pip install -U fiftyone albumentations\n

Next, install the FiftyOne Albumentations plugin:

Bash
fiftyone plugins download https://github.com/jacobmarks/fiftyone-albumentations-plugin\n

Note

If you have the FiftyOne Plugin Utils plugin installed, you can also install the Albumentations plugin via the install_plugin operator, selecting the Albumentations plugin from the community dropdown menu.

You will also need to load (and download if necessary) a dataset to apply the augmentations to. For this guide, we'll use the the quickstart dataset:

Python
import fiftyone as fo\nimport fiftyone.zoo as foz\n\n## only take 5 samples for quick demonstration\ndataset = foz.load_zoo_dataset(\"quickstart\", max_samples=5)\n\n# only keep the ground truth labels\ndataset.select_fields(\"ground_truth\").keep_fields()\n\nsession = fo.launch_app(dataset)\n

Note

The quickstart dataset only contains Detections labels. If you want to test Albumentations transformations on other label types, here are some quick examples to get you started, using FiftyOne's Hugging Face Transformers and Ultralytics integrations: Bash

pip install -U transformers ultralytics\n
Python
import fiftyone as fo\nimport fiftyone.zoo as foz\n\nfrom ultralytics import YOLO\n\n# Keypoints\nmodel = YOLO(\"yolov8l-pose.pt\")\ndataset.apply_model(model, label_field=\"keypoints\")\n\n# Instance Segmentation\nmodel = YOLO(\"yolov8l-seg.pt\")\ndataset.apply_model(model, label_field=\"instances\")\n\n# Semantic Segmentation\nmodel = foz.load_zoo_model(\n    \"segmentation-transformer-torch\",\n    name_or_path=\"Intel/dpt-large-ade\",\n)\ndataset.apply_model(model, label_field=\"mask\")\n\n# Heatmap\nmodel = foz.load_zoo_model(\n    \"depth-estimation-transformer-torch\",\n    name_or_path=\"LiheYoung/depth-anything-small-hf\",\n)\ndataset.apply_model(model, label_field=\"depth_map\")\n

"},{"location":"integrations/fiftyone/#apply-transformations","title":"Apply transformations","text":"

To apply Albumentations transformations to your dataset, you can use the augment_with_albumentations operator. Press the backtick key to open the operator modal, and select the augment_with_albumentations operator from the dropdown menu.

You can then configure the transformations to apply:

  • Number of augmentations per sample: The number of augmented samples to generate for each input sample. The default is 1, which is sufficient for deterministic transformations, but for probabilistic transformations, you may want to generate multiple samples to see the range of possible outputs.
  • Number of transforms: The number of transformations to compose into the pipeline to be applied to each sample. The default is 1, but you can set this as high as you'd like \u2014 the more transformations, the more complex the augmentations will be. You will be able to configure each transform separately.
  • Target view: The view to which the transformations will be applied. The default is dataset, but you can also apply the transformations to the current view or to currently selected samples within the app.
  • Execution mode: If you set delegated=False, the operation will be executed immediately. If you set delegated=True, the operation will be queued as a job, which you can then run in the background from your terminal with:
Bash
fiftyone delegated launch\n

For each transformation, you can select either a \"primitive\" transformation from the Albumentations library, or a \"saved\" transformation pipeline that you have previously saved to the dataset. These saved pipelines can consist of one or more transformations.

When you apply a primitive transformation, you can configure the parameters of the transformation directly within the app. The available parameters, their default values, types, and docstrings are all integrated directly from the Albumentations library.

When you apply a saved pipeline, there will not be any parameters to configure.

"},{"location":"integrations/fiftyone/#visualize-transformations","title":"Visualize transformations","text":"

Once you've applied the transformations, you can visualize the effects of the transformations directly within the FiftyOne App. All augmented samples will be added to the dataset, and will be tagged as augmented so that you can easily filter for just augmented or non-augmented samples in the app.

You can also filter for augmented samples programmatically with the match_tags() method:

Python
# get just the augmented samples\naugmented_view = dataset.match_tags(\"augmented\")\n\n# get just the non-augmented samples\nnon_augmented_view = dataset.match_tags(\"augmented\", bool=False)\n

However, matching on these tags will return all samples that have been generated by an augmentation, not just the samples that were generated by the last applied transformation \u2014 as you will see shortly, we can save augmentations to the dataset. To get just the samples generated by the last applied transformation, you can use the view_last_albumentations_run operator:

Note

For all samples added to the dataset by the FiftyOne Albumentations plugin, there will be a field \"transform\", which contains the information not just about the pipeline that was applied, but also about the specific parameters that were used for this application of the pipeline. For example, if you had a HorizontalFlip transformation with an application probability of p=0.5, the contents of the \"transform\" field tell you whether or not this transformation was applied to the sample!

"},{"location":"integrations/fiftyone/#save-augmentations","title":"Save augmentations","text":"

By default all augmentations are temporary, as the FiftyOne Albumentations plugin is primarily designed for rapid prototyping and experimentation. This means that when you generated a new batch of augmented samples, the previous batch of augmented samples will be removed from the dataset, and the image files will be deleted from disk.

However, if you want to save the augmented samples to the dataset, you can use the save_albumentations_augmentations operator, which will save the augmented samples to the dataset while keeping the augmented tag on the samples.

"},{"location":"integrations/fiftyone/#get-last-transformation-info","title":"Get last transformation info","text":"

When you apply a transformation pipeline to samples in your dataset using the FiftyOne Albumentations plugin, this information is captured and stored using FiftyOne's custom runs. This means that you can easily access the information about the last applied transformation.

In the FiftyOne App, you can use the get_last_albumentations_run_info operator to display a formatted summary of the relevant information:

Note

You can also access this information programmatically by getting info about the custom run that the information is stored in. For the Albumentations plugin, this info is stored via the key '_last_albumentations_run':

Python
last_run_info = dataset.get_run_info(\"_last_albumentations_run\")\nprint(last_run_info)\n
"},{"location":"integrations/fiftyone/#save-transformations","title":"Save transformations","text":"

If you are satisfied with the transformation pipeline you have created, you can save the entire composition of transformations to the dataset, hyperparameters and all. This means that after your rapid prototyping phase, you can easily move to a more reproducible workflow, and you can share your transformations or port them to other datasets.

To save a transformation pipeline, you can use the save_albumentations_transform operator:

After doing so, you will be able to view the information about this saved transformation pipeline using the get_albumentations_run_info operator:

Additionally, you will have access to this saved transformation pipeline under the \"saved\" tab for each transformation in the augment_with_albumentations operator modal.

"},{"location":"integrations/huggingface/","title":"HuggingFace","text":"
  • Image classification
  • Object Detection
"},{"location":"integrations/huggingface/image_classification_albumentations/","title":"Fine-tuning for Image Classification with \ud83e\udd17 Transformers","text":"

This notebook shows how to fine-tune any pretrained Vision model for Image Classification on a custom dataset. The idea is to add a randomly initialized classification head on top of a pre-trained encoder, and fine-tune the model altogether on a labeled dataset.

"},{"location":"integrations/huggingface/image_classification_albumentations/#imagefolder-feature","title":"ImageFolder feature","text":"

This notebook leverages the ImageFolder feature to easily run the notebook on a custom dataset (namely, EuroSAT in this tutorial). You can either load a Dataset from local folders or from local/remote files, like zip or tar.

"},{"location":"integrations/huggingface/image_classification_albumentations/#any-model","title":"Any model","text":"

This notebook is built to run on any image classification dataset with any vision model checkpoint from the Model Hub as long as that model has a version with a Image Classification head, such as: * ViT * Swin Transformer * ConvNeXT

  • in short, any model supported by AutoModelForImageClassification.
"},{"location":"integrations/huggingface/image_classification_albumentations/#albumentations","title":"Albumentations","text":"

In this notebook, we are going to leverage the Albumentations library for data augmentation. Note that we have other versions of this notebook available as well with other libraries including:

  • Torchvision's Transforms
  • Kornia
  • imgaug.

Depending on the model and the GPU you are using, you might need to adjust the batch size to avoid out-of-memory errors. Set those two parameters, then the rest of the notebook should run smoothly.

In this notebook, we'll fine-tune from the https://huggingface.co/facebook/convnext-tiny-224 checkpoint, but note that there are many, many more available on the hub.

Python
model_checkpoint = \"facebook/convnext-tiny-224\" # pre-trained model from which to fine-tune\nbatch_size = 32 # batch size for training and evaluation\n

Before we start, let's install the datasets, transformers and albumentations libraries.

Python
!pip install -q datasets transformers\n
\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 325 kB 8.7 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 4.0 MB 67.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 77 kB 8.1 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 1.1 MB 48.8 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 136 kB 72.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 212 kB 72.9 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 127 kB 75.0 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 895 kB 67.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 6.5 MB 56.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 596 kB 76.4 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 144 kB 76.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 94 kB 3.3 MB/s \n\u001b[K     |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 271 kB 77.3 MB/s \n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\ndatascience 0.10.6 requires folium==0.2.1, but you have folium 0.8.3 which is incompatible.\u001b[0m\n\u001b[?25h\n
Python
!pip install -q albumentations\n
\u001b[?25l\n

\u001b[K |\u258c | 10 kB 26.1 MB/s eta 0:00:01 \u001b[K |\u2588 | 20 kB 27.6 MB/s eta 0:00:01 \u001b[K |\u2588\u258b | 30 kB 11.8 MB/s eta 0:00:01 \u001b[K |\u2588\u2588 | 40 kB 8.9 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u258b | 51 kB 6.7 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u258f | 61 kB 7.9 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u258b | 71 kB 8.0 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u258f | 81 kB 7.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u258a | 92 kB 8.2 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u258f | 102 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u258a | 112 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 122 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 133 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 143 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 153 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 163 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 174 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 184 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 194 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 204 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 215 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 225 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 235 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 245 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 256 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 266 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 276 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 286 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 296 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 307 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 317 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 327 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 337 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 348 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 358 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 368 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f | 378 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 389 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 399 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258a | 409 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 419 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 430 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258e | 440 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 450 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 460 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2589 | 471 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 481 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 491 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258d | 501 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 512 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 522 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 532 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 542 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 552 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 563 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 573 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 583 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 | 593 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b | 604 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258f| 614 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258b| 624 kB 8.4 MB/s eta 0:00:01 \u001b[K |\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 631 kB 8.4 MB/s \u001b[?25h Building wheel for imgaug (setup.py) ... \u001b[?25l\u001b[?25hdone

If you're opening this notebook locally, make sure your environment has an install from the last version of those libraries.

To be able to share your model with the community and generate results like the one shown in the picture below via the inference API, there are a few more steps to follow.

First you have to store your authentication token from the Hugging Face website (sign up here if you haven't already!) then execute the following cell and input your token:

Python
from huggingface_hub import notebook_login\n\nnotebook_login()\n
Login successful\nYour token has been saved to /root/.huggingface/token\n\u001b[1m\u001b[31mAuthenticated through git-credential store but this isn't the helper defined on your machine.\nYou might have to re-authenticate when pushing to the Hugging Face Hub. Run the following command in your terminal in case you want to set this credential helper as the default\n\ngit config --global credential.helper store\u001b[0m\n

Then you need to install Git-LFS to upload your model checkpoints:

Python
%%capture\n!sudo apt -qq install git-lfs\n!git config --global credential.helper store\n

We also quickly upload some telemetry - this tells us which examples and software versions are getting used so we know where to prioritize our maintenance efforts. We don't collect (or care about) any personally identifiable information, but if you'd prefer not to be counted, feel free to skip this step or delete this cell entirely.

Python
from transformers.utils import send_example_telemetry\n\nsend_example_telemetry(\"image_classification_albumentations_notebook\", framework=\"pytorch\")\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#fine-tuning-a-model-on-an-image-classification-task","title":"Fine-tuning a model on an image classification task","text":"

In this notebook, we will see how to fine-tune one of the \ud83e\udd17 Transformers vision models on an Image Classification dataset.

Given an image, the goal is to predict an appropriate class for it, like \"tiger\". The screenshot below is taken from a ViT fine-tuned on ImageNet-1k - try out the inference widget!

"},{"location":"integrations/huggingface/image_classification_albumentations/#loading-the-dataset","title":"Loading the dataset","text":"

We will use the \ud83e\udd17 Datasets library's ImageFolder feature to download our custom dataset into a DatasetDict.

In this case, the EuroSAT dataset is hosted remotely, so we provide the data_files argument. Alternatively, if you have local folders with images, you can load them using the data_dir argument.

Python
from datasets import load_dataset \n\n# load a custom dataset from local/remote files using the ImageFolder feature\n\n# option 1: local/remote files (supporting the following formats: tar, gzip, zip, xz, rar, zstd)\ndataset = load_dataset(\"imagefolder\", data_files=\"https://madm.dfki.de/files/sentinel/EuroSAT.zip\")\n\n# note that you can also provide several splits:\n# dataset = load_dataset(\"imagefolder\", data_files={\"train\": [\"path/to/file1\", \"path/to/file2\"], \"test\": [\"path/to/file3\", \"path/to/file4\"]})\n\n# note that you can push your dataset to the hub very easily (and reload afterwards using load_dataset)!\n# dataset.push_to_hub(\"nielsr/eurosat\")\n# dataset.push_to_hub(\"nielsr/eurosat\", private=True)\n\n# option 2: local folder\n# dataset = load_dataset(\"imagefolder\", data_dir=\"path_to_folder\")\n\n# option 3: just load any existing dataset from the hub ...\n# dataset = load_dataset(\"cifar10\")\n
Using custom data configuration default-0537267e6f812d56\n\n\nDownloading and preparing dataset image_folder/default to /root/.cache/huggingface/datasets/image_folder/default-0537267e6f812d56/0.0.0/ee92df8e96c6907f3c851a987be3fd03d4b93b247e727b69a8e23ac94392a091...\n\n\n\nDownloading data files: 0it [00:00, ?it/s]\n\n\n\nDownloading data files:   0%|          | 0/1 [00:00<?, ?it/s]\n\n\n\nDownloading data:   0%|          | 0.00/94.3M [00:00<?, ?B/s]\n\n\n\nExtracting data files:   0%|          | 0/1 [00:00<?, ?it/s]\n\n\n\nGenerating train split: 0 examples [00:00, ? examples/s]\n\n\nDataset image_folder downloaded and prepared to /root/.cache/huggingface/datasets/image_folder/default-0537267e6f812d56/0.0.0/ee92df8e96c6907f3c851a987be3fd03d4b93b247e727b69a8e23ac94392a091. Subsequent calls will reuse this data.\n\n\n\n  0%|          | 0/1 [00:00<?, ?it/s]\n

Let us also load the Accuracy metric, which we'll use to evaluate our model both during and after training.

Python
from datasets import load_metric\n\nmetric = load_metric(\"accuracy\")\n
Downloading builder script:   0%|          | 0.00/1.41k [00:00<?, ?B/s]\n

The dataset object itself is a DatasetDict, which contains one key per split (in this case, only \"train\" for a training split).

Python
dataset\n
DatasetDict({\n    train: Dataset({\n        features: ['image', 'label'],\n        num_rows: 27000\n    })\n})\n

To access an actual element, you need to select a split first, then give an index:

Python
example = dataset[\"train\"][10]\nexample\n
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=64x64 at 0x7FD62DA6B2D0>,\n 'label': 2}\n

Each example consists of an image and a corresponding label. We can also verify this by checking the features of the dataset:

Python
dataset[\"train\"].features\n
{'image': Image(decode=True, id=None),\n 'label': ClassLabel(num_classes=10, names=['AnnualCrop', 'Forest', 'HerbaceousVegetation', 'Highway', 'Industrial', 'Pasture', 'PermanentCrop', 'Residential', 'River', 'SeaLake'], id=None)}\n

The cool thing is that we can directly view the image (as the 'image' field is an Image feature), as follows:

Python
example['image']\n

Let's make it a little bigger as the images in the EuroSAT dataset are of low resolution (64x64 pixels):

Python
example['image'].resize((200, 200))\n

Let's check the corresponding label:

Python
example['label']\n
2\n

As you can see, the label field is not an actual string label. By default the ClassLabel fields are encoded into integers for convenience:

Python
dataset[\"train\"].features[\"label\"]\n
ClassLabel(num_classes=10, names=['AnnualCrop', 'Forest', 'HerbaceousVegetation', 'Highway', 'Industrial', 'Pasture', 'PermanentCrop', 'Residential', 'River', 'SeaLake'], id=None)\n

Let's create an id2label dictionary to decode them back to strings and see what they are. The inverse label2id will be useful too, when we load the model later.

Python
labels = dataset[\"train\"].features[\"label\"].names\nlabel2id, id2label = dict(), dict()\nfor i, label in enumerate(labels):\n    label2id[label] = i\n    id2label[i] = label\n\nid2label[2]\n
'HerbaceousVegetation'\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#preprocessing-the-data","title":"Preprocessing the data","text":"

Before we can feed these images to our model, we need to preprocess them.

Preprocessing images typically comes down to (1) resizing them to a particular size (2) normalizing the color channels (R,G,B) using a mean and standard deviation. These are referred to as image transformations.

In addition, one typically performs what is called data augmentation during training (like random cropping and flipping) to make the model more robust and achieve higher accuracy. Data augmentation is also a great technique to increase the size of the training data.

We will use Albumentations for the image transformations/data augmentation in this tutorial, but note that one can use any other package (like torchvision's transforms, imgaug, Kornia, etc.).

To make sure we (1) resize to the appropriate size (2) use the appropriate image mean and standard deviation for the model architecture we are going to use, we instantiate what is called an image processor with the AutoImageProcessor.from_pretrained method.

This image processor is a minimal preprocessor that can be used to prepare images for inference.

Python
from transformers import AutoImageProcessor\n\nimage_processor = AutoImageProcessor.from_pretrained(model_checkpoint)\nimage_processor\n
Could not find image processor class in the image processor config or the model config. Loading based on pattern matching with the model's feature extractor configuration.\n\n\n\n\n\nConvNextImageProcessor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_rescale\": true,\n  \"do_resize\": true,\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_processor_type\": \"ConvNextImageProcessor\",\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"rescale_factor\": 0.00392156862745098,\n  \"size\": {\n    \"shortest_edge\": 224\n  }\n}\n

The Datasets library is made for processing data very easily. We can write custom functions, which can then be applied on an entire dataset (either using .map() or .set_transform()).

Here we define 2 separate functions, one for training (which includes data augmentation) and one for validation (which only includes resizing, center cropping and normalizing).

Python
import cv2\nimport albumentations as A\nimport numpy as np\n\nif \"height\" in image_processor.size:\n    size = (image_processor.size[\"height\"], image_processor.size[\"width\"])\n    crop_size = size\n    max_size = None\nelif \"shortest_edge\" in image_processor.size:\n    size = image_processor.size[\"shortest_edge\"]\n    crop_size = (size, size)\n    max_size = image_processor.size.get(\"longest_edge\")\n\ntrain_transforms = A.Compose([\n    A.Resize(height=size, width=size),\n    A.RandomRotate90(),\n    A.HorizontalFlip(p=0.5),\n    A.RandomBrightnessContrast(p=0.2),\n    A.Normalize(),\n])\n\nval_transforms = A.Compose([\n    A.Resize(height=size, width=size),\n    A.Normalize(),\n])\n\ndef preprocess_train(examples):\n    examples[\"pixel_values\"] = [\n        train_transforms(image=np.array(image))[\"image\"] for image in examples[\"image\"]\n    ]\n\n    return examples\n\ndef preprocess_val(examples):\n    examples[\"pixel_values\"] = [\n        val_transforms(image=np.array(image))[\"image\"] for image in examples[\"image\"]\n    ]\n\n    return examples\n

Next, we can preprocess our dataset by applying these functions. We will use the set_transform functionality, which allows to apply the functions above on-the-fly (meaning that they will only be applied when the images are loaded in RAM).

Python
# split up training into training + validation\nsplits = dataset[\"train\"].train_test_split(test_size=0.1)\ntrain_ds = splits['train']\nval_ds = splits['test']\n
Python
train_ds.set_transform(preprocess_train)\nval_ds.set_transform(preprocess_val)\n

Let's check the first example:

Python
train_ds[0]\n
{'image': <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=64x64 at 0x7FD610178490>,\n 'label': 5,\n 'pixel_values': array([[[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052]],\n\n        [[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052],\n         [-1.34729   , -0.897759  , -0.37525052]],\n\n        [[-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         [-1.415789  , -0.53011197, -0.37525052],\n         ...,\n         [-1.3986642 , -0.93277305, -0.4101089 ],\n         [-1.3986642 , -0.93277305, -0.4101089 ],\n         [-1.3986642 , -0.93277305, -0.4101089 ]],\n\n        ...,\n\n        [[-1.5014129 , -0.582633  , -0.35782132],\n         [-1.5014129 , -0.582633  , -0.35782132],\n         [-1.5014129 , -0.582633  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]],\n\n        [[-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]],\n\n        [[-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.3403921 ],\n         [-1.5356623 , -0.565126  , -0.35782132],\n         ...,\n         [-1.4842881 , -0.98529404, -0.5146841 ],\n         [-1.4671633 , -1.0028011 , -0.49725488],\n         [-1.4671633 , -1.0028011 , -0.49725488]]], dtype=float32)}\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#training-the-model","title":"Training the model","text":"

Now that our data is ready, we can download the pretrained model and fine-tune it. For classification we use the AutoModelForImageClassification class. Like with the image processor, the from_pretrained method will download and cache the model for us. As the label ids and the number of labels are dataset dependent, we pass num_labels, label2id, and id2label alongside the model_checkpoint he\u00a3re.

NOTE: in case you're planning to fine-tune an already fine-tuned checkpoint, like facebook/convnext-tiny-224 (which has already been fine-tuned on ImageNet-1k), then you need to provide the additional argument ignore_mismatched_sizes=True to the from_pretrained method. This will make sure the output head is thrown away and replaced by a new, randomly initialized classification head that includes a custom number of output neurons.

Python
from transformers import AutoModelForImageClassification, TrainingArguments, Trainer\n\nnum_labels = len(id2label)\nmodel = AutoModelForImageClassification.from_pretrained(\n    model_checkpoint, \n    label2id=label2id,\n    id2label=id2label,\n    ignore_mismatched_sizes = True, # provide this in case you'd like to fine-tune an already fine-tuned checkpoint\n)\n
Downloading:   0%|          | 0.00/68.0k [00:00<?, ?B/s]\n\n\n\nDownloading:   0%|          | 0.00/109M [00:00<?, ?B/s]\n\n\nSome weights of ConvNextForImageClassification were not initialized from the model checkpoint at facebook/convnext-tiny-224 and are newly initialized because the shapes did not match:\n- classifier.weight: found shape torch.Size([1000, 768]) in the checkpoint and torch.Size([10, 768]) in the model instantiated\n- classifier.bias: found shape torch.Size([1000]) in the checkpoint and torch.Size([10]) in the model instantiated\nYou should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n

The warning is telling us we are throwing away some weights (the weights and bias of the pooler layer) and randomly initializing some other (the weights and bias of the classifier layer). This is expected in this case, because we are adding a new head for which we don't have pretrained weights, so the library warns us we should fine-tune this model before using it for inference, which is exactly what we are going to do.

To instantiate a Trainer, we will need to define the training configuration and the evaluation metric. The most important is the TrainingArguments, which is a class that contains all the attributes to customize the training. It requires one folder name, which will be used to save the checkpoints of the model.

Most of the training arguments are pretty self-explanatory, but one that is quite important here is remove_unused_columns=False. This one will drop any features not used by the model's call function. By default it's True because usually it's ideal to drop unused feature columns, making it easier to unpack inputs into the model's call function. But, in our case, we need the unused features ('img' in particular) in order to create 'pixel_values'.

Python
model_name = model_checkpoint.split(\"/\")[-1]\n\nargs = TrainingArguments(\n    f\"{model_name}-finetuned-eurosat-albumentations\",\n    remove_unused_columns=False,\n    evaluation_strategy = \"epoch\",\n    save_strategy = \"epoch\",\n    learning_rate=5e-5,\n    per_device_train_batch_size=batch_size,\n    gradient_accumulation_steps=4,\n    per_device_eval_batch_size=batch_size,\n    num_train_epochs=3,\n    warmup_ratio=0.1,\n    logging_steps=10,\n    load_best_model_at_end=True,\n    metric_for_best_model=\"accuracy\",\n    push_to_hub=True,\n)\n

Here we set the evaluation to be done at the end of each epoch, tweak the learning rate, use the batch_size defined at the top of the notebook and customize the number of epochs for training, as well as the weight decay. Since the best model might not be the one at the end of training, we ask the Trainer to load the best model it saved (according to metric_name) at the end of training.

The last argument push_to_hub allows the Trainer to push the model to the Hub regularly during training. Remove it if you didn't follow the installation steps at the top of the notebook. If you want to save your model locally with a name that is different from the name of the repository, or if you want to push your model under an organization and not your name space, use the hub_model_id argument to set the repo name (it needs to be the full name, including your namespace: for instance \"nielsr/vit-finetuned-cifar10\" or \"huggingface/nielsr/vit-finetuned-cifar10\").

Next, we need to define a function for how to compute the metrics from the predictions, which will just use the metric we loaded earlier. The only preprocessing we have to do is to take the argmax of our predicted logits:

Python
import numpy as np\n\n# the compute_metrics function takes a Named Tuple as input:\n# predictions, which are the logits of the model as Numpy arrays,\n# and label_ids, which are the ground-truth labels as Numpy arrays.\ndef compute_metrics(eval_pred):\n    \"\"\"Computes accuracy on a batch of predictions\"\"\"\n    predictions = np.argmax(eval_pred.predictions, axis=1)\n    return metric.compute(predictions=predictions, references=eval_pred.label_ids)\n

We also define a collate_fn, which will be used to batch examples together. Each batch consists of 2 keys, namely pixel_values and labels.

Python
import torch\n\ndef collate_fn(examples):\n    images = []\n    labels = []\n    for example in examples:\n        image = np.moveaxis(example[\"pixel_values\"], source=2, destination=0)\n        images.append(torch.from_numpy(image))\n        labels.append(example[\"label\"])\n\n    pixel_values = torch.stack(images)\n    labels = torch.tensor(labels)\n    return {\"pixel_values\": pixel_values, \"labels\": labels}\n

Then we just need to pass all of this along with our datasets to the Trainer:

Python
trainer = Trainer(\n    model,\n    args,\n    train_dataset=train_ds,\n    eval_dataset=val_ds,\n    tokenizer=image_processor,\n    compute_metrics=compute_metrics,\n    data_collator=collate_fn,\n)\n
/content/convnext-tiny-224-finetuned-eurosat-albumentations is already a clone of https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations. Make sure you pull the latest changes with `repo.git_pull()`.\n

You might wonder why we pass along the image_processor as a tokenizer when we already preprocessed our data. This is only to make sure the image processor configuration file (stored as JSON) will also be uploaded to the repo on the hub.

Now we can finetune our model by calling the train method:

Python
trainer.train()\n
/usr/local/lib/python3.7/dist-packages/transformers/optimization.py:309: FutureWarning: This implementation of AdamW is deprecated and will be removed in a future version. Use the PyTorch implementation torch.optim.AdamW instead, or set `no_deprecation_warning=True` to disable this warning\n  FutureWarning,\n***** Running training *****\n  Num examples = 24300\n  Num Epochs = 3\n  Instantaneous batch size per device = 32\n  Total train batch size (w. parallel, distributed & accumulation) = 128\n  Gradient Accumulation steps = 4\n  Total optimization steps = 570\n\n\n\n\n<div>\n\n  <progress value='570' max='570' style='width:300px; height:20px; vertical-align: middle;'></progress>\n  [570/570 15:59, Epoch 3/3]\n</div>\n<table border=\"1\" class=\"dataframe\">\n
Epoch Training Loss Validation Loss Accuracy 1 0.141000 0.149633 0.954444 2 0.073600 0.095782 0.971852 3 0.056800 0.072716 0.974815

***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-190/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-380/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\nSaving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570/preprocessor_config.json\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n\n\nTraining completed. Do not forget to share your model on huggingface.co/models =)\n\n\nLoading best model from convnext-tiny-224-finetuned-eurosat-albumentations/checkpoint-570 (score: 0.9748148148148148).\n\n\n\n\n\nTrainOutput(global_step=570, training_loss=0.34729809766275843, metrics={'train_runtime': 961.6293, 'train_samples_per_second': 75.809, 'train_steps_per_second': 0.593, 'total_flos': 1.8322098956292096e+18, 'train_loss': 0.34729809766275843, 'epoch': 3.0})\n

We can check with the evaluate method that our Trainer did reload the best model properly (if it was not the last one):

Python
metrics = trainer.evaluate()\nprint(metrics)\n
***** Running Evaluation *****\n  Num examples = 2700\n  Batch size = 32\n
[85/85 00:12]
{'eval_loss': 0.0727163776755333, 'eval_accuracy': 0.9748148148148148, 'eval_runtime': 13.0419, 'eval_samples_per_second': 207.026, 'eval_steps_per_second': 6.517, 'epoch': 3.0}\n

You can now upload the result of the training to the Hub, just execute this instruction (note that the Trainer will automatically create a model card for you, as well as adding Tensorboard metrics - see the \"Training metrics\" tab!):

Python
trainer.push_to_hub()\n
Saving model checkpoint to convnext-tiny-224-finetuned-eurosat-albumentations\nConfiguration saved in convnext-tiny-224-finetuned-eurosat-albumentations/config.json\nModel weights saved in convnext-tiny-224-finetuned-eurosat-albumentations/pytorch_model.bin\nFeature extractor saved in convnext-tiny-224-finetuned-eurosat-albumentations/preprocessor_config.json\n\n\n\nUpload file runs/Apr12_12-03-24_1ad162e1ead9/events.out.tfevents.1649765159.1ad162e1ead9.73.4:  24%|##4       \u2026\n\n\n\nUpload file runs/Apr12_12-03-24_1ad162e1ead9/events.out.tfevents.1649767032.1ad162e1ead9.73.6: 100%|##########\u2026\n\n\nTo https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\n   c500b3f..2143b42  main -> main\n\nTo https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\n   2143b42..71339cf  main -> main\n\n\n\n\n\n\n'https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/commit/2143b423b5cacdde6daebd3ee2b5971ecab463f6'\n

You can now share this model with all your friends, family, favorite pets: they can all load it with the identifier \"your-username/the-name-you-picked\" so for instance:

Python
from transformers import AutoModelForImageClassification, AutoImageProcessor\n\nimage_processor = AutoImageProcessor.from_pretrained(\"nielsr/my-awesome-model\")\nmodel = AutoModelForImageClassification.from_pretrained(\"nielsr/my-awesome-model\")\n
"},{"location":"integrations/huggingface/image_classification_albumentations/#inference","title":"Inference","text":"

Let's say you have a new image, on which you'd like to make a prediction. Let's load a satellite image of a highway (that's not part of the EuroSAT dataset), and see how the model does.

Python
from PIL import Image\nimport requests\n\nurl = 'https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/highway.jpg'\nimage = Image.open(requests.get(url, stream=True).raw)\nimage\n

We'll load the image processor and model from the hub (here, we use the Auto Classes, which will make sure the appropriate classes will be loaded automatically based on the config.json and preprocessor_config.json files of the repo on the hub):

Python
from transformers import AutoModelForImageClassification, AutoImageProcessor\n\nrepo_name = \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\"\n\nimage_processor = AutoImageProcessor.from_pretrained(repo_name)\nmodel = AutoModelForImageClassification.from_pretrained(repo_name)\n
https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmp04g0zg5n\n\n\n\nDownloading:   0%|          | 0.00/266 [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json in cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\ncreating metadata file for /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nloading feature extractor configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json from cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nFeature extractor ConvNextFeatureExtractor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_resize\": true,\n  \"feature_extractor_type\": \"ConvNextFeatureExtractor\",\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"size\": 224\n}\n\nhttps://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmpbf9y4q39\n\n\n\nDownloading:   0%|          | 0.00/1.03k [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json in cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\ncreating metadata file for /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nloading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nhttps://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin not found in cache or force_download set to True, downloading to /root/.cache/huggingface/transformers/tmpzr_9yxjo\n\n\n\nDownloading:   0%|          | 0.00/106M [00:00<?, ?B/s]\n\n\nstoring https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin in cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\ncreating metadata file for /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nloading weights file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin from cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nAll model checkpoint weights were used when initializing ConvNextForImageClassification.\n\nAll the weights of ConvNextForImageClassification were initialized from the model checkpoint at nielsr/convnext-tiny-224-finetuned-eurosat-albumentations.\nIf your task is similar to the task the model of the checkpoint was trained on, you can already use ConvNextForImageClassification for predictions without further training.\n
Python
# prepare image for the model\nencoding = image_processor(image.convert(\"RGB\"), return_tensors=\"pt\")\nprint(encoding.pixel_values.shape)\n
torch.Size([1, 3, 224, 224])\n
Python
import torch\n\n# forward pass\nwith torch.no_grad():\n    outputs = model(**encoding)\n    logits = outputs.logits\n
Python
predicted_class_idx = logits.argmax(-1).item()\nprint(\"Predicted class:\", model.config.id2label[predicted_class_idx])\n
Predicted class: Highway\n

Looks like our model got it correct!

"},{"location":"integrations/huggingface/image_classification_albumentations/#pipeline-api","title":"Pipeline API","text":"

An alternative way to quickly perform inference with any model on the hub is by leveraging the Pipeline API, which abstracts away all the steps we did manually above for us. It will perform the preprocessing, forward pass and postprocessing all in a single object.

Let's showcase this for our trained model:

Python
from transformers import pipeline\n\npipe = pipeline(\"image-classification\", \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\")\n
loading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nloading configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/config.json from cache at /root/.cache/huggingface/transformers/25088566ab29cf0ff360b05880b5f20cdc0c79ab995056a1fb4f98212d021154.4637c3f271a8dfbcfe5c4ee777270112d841a5af95814f0fd086c3c2761e7370\nModel config ConvNextConfig {\n  \"_name_or_path\": \"nielsr/convnext-tiny-224-finetuned-eurosat-albumentations\",\n  \"architectures\": [\n    \"ConvNextForImageClassification\"\n  ],\n  \"depths\": [\n    3,\n    3,\n    9,\n    3\n  ],\n  \"drop_path_rate\": 0.0,\n  \"hidden_act\": \"gelu\",\n  \"hidden_sizes\": [\n    96,\n    192,\n    384,\n    768\n  ],\n  \"id2label\": {\n    \"0\": \"AnnualCrop\",\n    \"1\": \"Forest\",\n    \"2\": \"HerbaceousVegetation\",\n    \"3\": \"Highway\",\n    \"4\": \"Industrial\",\n    \"5\": \"Pasture\",\n    \"6\": \"PermanentCrop\",\n    \"7\": \"Residential\",\n    \"8\": \"River\",\n    \"9\": \"SeaLake\"\n  },\n  \"image_size\": 224,\n  \"initializer_range\": 0.02,\n  \"label2id\": {\n    \"AnnualCrop\": 0,\n    \"Forest\": 1,\n    \"HerbaceousVegetation\": 2,\n    \"Highway\": 3,\n    \"Industrial\": 4,\n    \"Pasture\": 5,\n    \"PermanentCrop\": 6,\n    \"Residential\": 7,\n    \"River\": 8,\n    \"SeaLake\": 9\n  },\n  \"layer_norm_eps\": 1e-12,\n  \"layer_scale_init_value\": 1e-06,\n  \"model_type\": \"convnext\",\n  \"num_channels\": 3,\n  \"num_stages\": 4,\n  \"patch_size\": 4,\n  \"problem_type\": \"single_label_classification\",\n  \"torch_dtype\": \"float32\",\n  \"transformers_version\": \"4.18.0\"\n}\n\nloading weights file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/pytorch_model.bin from cache at /root/.cache/huggingface/transformers/3f4bcce35d3279d19b07fb762859d89bce636d8f0235685031ef6494800b9769.d611c768c0b0939188b05c3d505f0b36c97aa57649d4637e3384992d3c5c0b89\nAll model checkpoint weights were used when initializing ConvNextForImageClassification.\n\nAll the weights of ConvNextForImageClassification were initialized from the model checkpoint at nielsr/convnext-tiny-224-finetuned-eurosat-albumentations.\nIf your task is similar to the task the model of the checkpoint was trained on, you can already use ConvNextForImageClassification for predictions without further training.\nloading feature extractor configuration file https://huggingface.co/nielsr/convnext-tiny-224-finetuned-eurosat-albumentations/resolve/main/preprocessor_config.json from cache at /root/.cache/huggingface/transformers/38b41a2c904b6ce5bb10403bf902ee4263144d862c5a602c83cd120c0c1ba0e6.37be7274d6b5860aee104bb1fbaeb0722fec3850a85bb2557ae9491f17f89433\nFeature extractor ConvNextFeatureExtractor {\n  \"crop_pct\": 0.875,\n  \"do_normalize\": true,\n  \"do_resize\": true,\n  \"feature_extractor_type\": \"ConvNextFeatureExtractor\",\n  \"image_mean\": [\n    0.485,\n    0.456,\n    0.406\n  ],\n  \"image_std\": [\n    0.229,\n    0.224,\n    0.225\n  ],\n  \"resample\": 3,\n  \"size\": 224\n}\n
Python
pipe(image)\n
[{'label': 'Highway', 'score': 0.5163754224777222},\n {'label': 'River', 'score': 0.11824000626802444},\n {'label': 'AnnualCrop', 'score': 0.05467210337519646},\n {'label': 'PermanentCrop', 'score': 0.05066365748643875},\n {'label': 'Industrial', 'score': 0.049283623695373535}]\n

As we can see, it does not only show the class label with the highest probability, but does return the top 5 labels, with their corresponding scores. Note that the pipelines also work with local models and image_processor:

Python
pipe = pipeline(\"image-classification\", \n                model=model,\n                feature_extractor=image_processor)\n
Python
pipe(image)\n
[{'label': 'Highway', 'score': 0.5163754224777222},\n {'label': 'River', 'score': 0.11824000626802444},\n {'label': 'AnnualCrop', 'score': 0.05467210337519646},\n {'label': 'PermanentCrop', 'score': 0.05066365748643875},\n {'label': 'Industrial', 'score': 0.049283623695373535}]\n
Python
\n
"},{"location":"integrations/huggingface/object_detection/","title":"Object Detection","text":""},{"location":"integrations/huggingface/object_detection/#object-detection","title":"Object detection","text":"

Object detection is the computer vision task of detecting instances (such as humans, buildings, or cars) in an image. Object detection models receive an image as input and output coordinates of the bounding boxes and associated labels of the detected objects. An image can contain multiple objects, each with its own bounding box and a label (e.g. it can have a car and a building), and each object can be present in different parts of an image (e.g. the image can have several cars). This task is commonly used in autonomous driving for detecting things like pedestrians, road signs, and traffic lights. Other applications include counting objects in images, image search, and more.

In this guide, you will learn how to:

  1. Finetune DETR, a model that combines a convolutional backbone with an encoder-decoder Transformer, on the CPPE-5 dataset.
  2. Use your finetuned model for inference.

To see all architectures and checkpoints compatible with this task, we recommend checking the task-page

Before you begin, make sure you have all the necessary libraries installed:

Bash
pip install -q datasets transformers accelerate timm\npip install -q -U albumentations>=1.4.5 torchmetrics pycocotools\n

You'll use \ud83e\udd17 Datasets to load a dataset from the Hugging Face Hub, \ud83e\udd17 Transformers to train your model, and albumentations to augment the data.

We encourage you to share your model with the community. Log in to your Hugging Face account to upload it to the Hub. When prompted, enter your token to log in:

Python
>>> from huggingface_hub import notebook_login\n\n>>> notebook_login()\n

To get started, we'll define global constants, namely the model name and image size. For this tutorial, we'll use the conditional DETR model due to its faster convergence. Feel free to select any object detection model available in the transformers library.

Python
>>> MODEL_NAME = \"microsoft/conditional-detr-resnet-50\"  # or \"facebook/detr-resnet-50\"\n>>> IMAGE_SIZE = 480\n
"},{"location":"integrations/huggingface/object_detection/#load-the-cppe-5-dataset","title":"Load the CPPE-5 dataset","text":"

The CPPE-5 dataset contains images with annotations identifying medical personal protective equipment (PPE) in the context of the COVID-19 pandemic.

Start by loading the dataset and creating a validation split from train:

Python
>>> from datasets import load_dataset\n\n>>> cppe5 = load_dataset(\"cppe-5\")\n\n>>> if \"validation\" not in cppe5:\n...     split = cppe5[\"train\"].train_test_split(0.15, seed=1337)\n...     cppe5[\"train\"] = split[\"train\"]\n...     cppe5[\"validation\"] = split[\"test\"]\n\n>>> cppe5\nDatasetDict({\n    train: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 850\n    })\n    test: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 29\n    })\n    validation: Dataset({\n        features: ['image_id', 'image', 'width', 'height', 'objects'],\n        num_rows: 150\n    })\n})\n

You'll see that this dataset has 1000 images for train and validation sets and a test set with 29 images.

To get familiar with the data, explore what the examples look like.

Python
>>> cppe5[\"train\"][0]\n{\n  'image_id': 366,\n  'image': <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=500x290>,\n  'width': 500,\n  'height': 500,\n  'objects': {\n    'id': [1932, 1933, 1934],\n    'area': [27063, 34200, 32431],\n    'bbox': [[29.0, 11.0, 97.0, 279.0],\n      [201.0, 1.0, 120.0, 285.0],\n      [382.0, 0.0, 113.0, 287.0]],\n    'category': [0, 0, 0]\n  }\n}\n

The examples in the dataset have the following fields: - image_id: the example image id - image: a PIL.Image.Image object containing the image - width: width of the image - height: height of the image - objects: a dictionary containing bounding box metadata for the objects in the image: - id: the annotation id - area: the area of the bounding box - bbox: the object's bounding box (in the COCO format ) - category: the object's category, with possible values including Coverall (0), Face_Shield (1), Gloves (2), Goggles (3) and Mask (4)

You may notice that the bbox field follows the COCO format, which is the format that the DETR model expects. However, the grouping of the fields inside objects differs from the annotation format DETR requires. You will need to apply some preprocessing transformations before using this data for training.

To get an even better understanding of the data, visualize an example in the dataset.

Python
>>> import numpy as np\n>>> import os\n>>> from PIL import Image, ImageDraw\n\n>>> image = cppe5[\"train\"][2][\"image\"]\n>>> annotations = cppe5[\"train\"][2][\"objects\"]\n>>> draw = ImageDraw.Draw(image)\n\n>>> categories = cppe5[\"train\"].features[\"objects\"].feature[\"category\"].names\n\n>>> id2label = {index: x for index, x in enumerate(categories, start=0)}\n>>> label2id = {v: k for k, v in id2label.items()}\n\n>>> for i in range(len(annotations[\"id\"])):\n...     box = annotations[\"bbox\"][i]\n...     class_idx = annotations[\"category\"][i]\n...     x, y, w, h = tuple(box)\n...     # Check if coordinates are normalized or not\n...     if max(box) > 1.0:\n...         # Coordinates are un-normalized, no need to re-scale them\n...         x1, y1 = int(x), int(y)\n...         x2, y2 = int(x + w), int(y + h)\n...     else:\n...         # Coordinates are normalized, re-scale them\n...         x1 = int(x * width)\n...         y1 = int(y * height)\n...         x2 = int((x + w) * width)\n...         y2 = int((y + h) * height)\n...     draw.rectangle((x, y, x + w, y + h), outline=\"red\", width=1)\n...     draw.text((x, y), id2label[class_idx], fill=\"white\")\n\n>>> image\n

To visualize the bounding boxes with associated labels, you can get the labels from the dataset's metadata, specifically the category field. You'll also want to create dictionaries that map a label id to a label class (id2label) and the other way around (label2id). You can use them later when setting up the model. Including these maps will make your model reusable by others if you share it on the Hugging Face Hub. Please note that, the part of above code that draws the bounding boxes assume that it is in COCO format (x_min, y_min, width, height). It has to be adjusted to work for other formats like (x_min, y_min, x_max, y_max).

As a final step of getting familiar with the data, explore it for potential issues. One common problem with datasets for object detection is bounding boxes that \"stretch\" beyond the edge of the image. Such \"runaway\" bounding boxes can raise errors during training and should be addressed. There are a few examples with this issue in this dataset. To keep things simple in this guide, we will set clip=True for BboxParams in transformations below.

"},{"location":"integrations/huggingface/object_detection/#preprocess-the-data","title":"Preprocess the data","text":"

To finetune a model, you must preprocess the data you plan to use to match precisely the approach used for the pre-trained model. [AutoImageProcessor] takes care of processing image data to create pixel_values, pixel_mask, and labels that a DETR model can train with. The image processor has some attributes that you won't have to worry about:

  • image_mean = [0.485, 0.456, 0.406 ]
  • image_std = [0.229, 0.224, 0.225]

These are the mean and standard deviation used to normalize images during the model pre-training. These values are crucial to replicate when doing inference or finetuning a pre-trained image model.

Instantiate the image processor from the same checkpoint as the model you want to finetune.

Python
>>> from transformers import AutoImageProcessor\n\n>>> MAX_SIZE = IMAGE_SIZE\n\n>>> image_processor = AutoImageProcessor.from_pretrained(\n...     MODEL_NAME,\n...     do_resize=True,\n...     size={\"max_height\": MAX_SIZE, \"max_width\": MAX_SIZE},\n...     do_pad=True,\n...     pad_size={\"height\": MAX_SIZE, \"width\": MAX_SIZE},\n... )\n

Before passing the images to the image_processor, apply two preprocessing transformations to the dataset: - Augmenting images - Reformatting annotations to meet DETR expectations

First, to make sure the model does not overfit on the training data, you can apply image augmentation with any data augmentation library. Here we use Albumentations. This library ensures that transformations affect the image and update the bounding boxes accordingly. The \ud83e\udd17 Datasets library documentation has a detailed guide on how to augment images for object detection, and it uses the exact same dataset as an example. Apply some geometric and color transformations to the image. For additional augmentation options, explore the Albumentations Demo Space.

Python
>>> import albumentations as A\n\n>>> train_augment_and_transform = A.Compose(\n...     [\n...         A.Perspective(p=0.1),\n...         A.HorizontalFlip(p=0.5),\n...         A.RandomBrightnessContrast(p=0.5),\n...         A.HueSaturationValue(p=0.1),\n...     ],\n...     bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"category\"], clip=True, min_area=25),\n... )\n\n>>> validation_transform = A.Compose(\n...     [A.NoOp()],\n...     bbox_params=A.BboxParams(format=\"coco\", label_fields=[\"category\"], clip=True),\n... )\n

The image_processor expects the annotations to be in the following format: {'image_id': int, 'annotations': List[Dict]}, where each dictionary is a COCO object annotation. Let's add a function to reformat annotations for a single example:

Python
>>> def format_image_annotations_as_coco(image_id, categories, areas, bboxes):\n...     \"\"\"Format one set of image annotations to the COCO format\n\n...     Args:\n...         image_id (str): image id. e.g. \"0001\"\n...         categories (List[int]): list of categories/class labels corresponding to provided bounding boxes\n...         areas (List[float]): list of corresponding areas to provided bounding boxes\n...         bboxes (List[Tuple[float]]): list of bounding boxes provided in COCO format\n...             ([center_x, center_y, width, height] in absolute coordinates)\n\n...     Returns:\n...         dict: {\n...             \"image_id\": image id,\n...             \"annotations\": list of formatted annotations\n...         }\n...     \"\"\"\n...     annotations = []\n...     for category, area, bbox in zip(categories, areas, bboxes):\n...         formatted_annotation = {\n...             \"image_id\": image_id,\n...             \"category_id\": category,\n...             \"iscrowd\": 0,\n...             \"area\": area,\n...             \"bbox\": list(bbox),\n...         }\n...         annotations.append(formatted_annotation)\n\n...     return {\n...         \"image_id\": image_id,\n...         \"annotations\": annotations,\n...     }\n

Now you can combine the image and annotation transformations to use on a batch of examples:

Python
>>> def augment_and_transform_batch(examples, transform, image_processor, return_pixel_mask=False):\n...     \"\"\"Apply augmentations and format annotations in COCO format for object detection task\"\"\"\n\n...     images = []\n...     annotations = []\n...     for image_id, image, objects in zip(examples[\"image_id\"], examples[\"image\"], examples[\"objects\"]):\n...         image = np.array(image.convert(\"RGB\"))\n\n...         # apply augmentations\n...         output = transform(image=image, bboxes=objects[\"bbox\"], category=objects[\"category\"])\n...         images.append(output[\"image\"])\n\n...         # format annotations in COCO format\n...         formatted_annotations = format_image_annotations_as_coco(\n...             image_id, output[\"category\"], objects[\"area\"], output[\"bboxes\"]\n...         )\n...         annotations.append(formatted_annotations)\n\n...     # Apply the image processor transformations: resizing, rescaling, normalization\n...     result = image_processor(images=images, annotations=annotations, return_tensors=\"pt\")\n\n...     if not return_pixel_mask:\n...         result.pop(\"pixel_mask\", None)\n\n...     return result\n

Apply this preprocessing function to the entire dataset using \ud83e\udd17 Datasets [~datasets.Dataset.with_transform] method. This method applies transformations on the fly when you load an element of the dataset.

At this point, you can check what an example from the dataset looks like after the transformations. You should see a tensor with pixel_values, a tensor with pixel_mask, and labels.

Python
>>> from functools import partial\n\n>>> # Make transform functions for batch and apply for dataset splits\n>>> train_transform_batch = partial(\n...     augment_and_transform_batch, transform=train_augment_and_transform, image_processor=image_processor\n... )\n>>> validation_transform_batch = partial(\n...     augment_and_transform_batch, transform=validation_transform, image_processor=image_processor\n... )\n\n>>> cppe5[\"train\"] = cppe5[\"train\"].with_transform(train_transform_batch)\n>>> cppe5[\"validation\"] = cppe5[\"validation\"].with_transform(validation_transform_batch)\n>>> cppe5[\"test\"] = cppe5[\"test\"].with_transform(validation_transform_batch)\n\n>>> cppe5[\"train\"][15]\n{'pixel_values': tensor([[[ 1.9235,  1.9407,  1.9749,  ..., -0.7822, -0.7479, -0.6965],\n          [ 1.9578,  1.9749,  1.9920,  ..., -0.7993, -0.7650, -0.7308],\n          [ 2.0092,  2.0092,  2.0263,  ..., -0.8507, -0.8164, -0.7822],\n          ...,\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741],\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741],\n          [ 0.0741,  0.0741,  0.0741,  ...,  0.0741,  0.0741,  0.0741]],\n\n          [[ 1.6232,  1.6408,  1.6583,  ...,  0.8704,  1.0105,  1.1331],\n          [ 1.6408,  1.6583,  1.6758,  ...,  0.8529,  0.9930,  1.0980],\n          [ 1.6933,  1.6933,  1.7108,  ...,  0.8179,  0.9580,  1.0630],\n          ...,\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052],\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052],\n          [ 0.2052,  0.2052,  0.2052,  ...,  0.2052,  0.2052,  0.2052]],\n\n          [[ 1.8905,  1.9080,  1.9428,  ..., -0.1487, -0.0964, -0.0615],\n          [ 1.9254,  1.9428,  1.9603,  ..., -0.1661, -0.1138, -0.0790],\n          [ 1.9777,  1.9777,  1.9951,  ..., -0.2010, -0.1138, -0.0790],\n          ...,\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265],\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265],\n          [ 0.4265,  0.4265,  0.4265,  ...,  0.4265,  0.4265,  0.4265]]]),\n  'labels': {'image_id': tensor([688]), 'class_labels': tensor([3, 4, 2, 0, 0]), 'boxes': tensor([[0.4700, 0.1933, 0.1467, 0.0767],\n          [0.4858, 0.2600, 0.1150, 0.1000],\n          [0.4042, 0.4517, 0.1217, 0.1300],\n          [0.4242, 0.3217, 0.3617, 0.5567],\n          [0.6617, 0.4033, 0.5400, 0.4533]]), 'area': tensor([ 4048.,  4140.,  5694., 72478., 88128.]), 'iscrowd': tensor([0, 0, 0, 0, 0]), 'orig_size': tensor([480, 480])}}\n

You have successfully augmented the individual images and prepared their annotations. However, preprocessing isn't complete yet. In the final step, create a custom collate_fn to batch images together. Pad images (which are now pixel_values) to the largest image in a batch, and create a corresponding pixel_mask to indicate which pixels are real (1) and which are padding (0).

Python
>>> import torch\n\n>>> def collate_fn(batch):\n...     data = {}\n...     data[\"pixel_values\"] = torch.stack([x[\"pixel_values\"] for x in batch])\n...     data[\"labels\"] = [x[\"labels\"] for x in batch]\n...     if \"pixel_mask\" in batch[0]:\n...         data[\"pixel_mask\"] = torch.stack([x[\"pixel_mask\"] for x in batch])\n...     return data\n
"},{"location":"integrations/huggingface/object_detection/#preparing-function-to-compute-map","title":"Preparing function to compute mAP","text":"

Object detection models are commonly evaluated with a set of COCO-style metrics. We are going to use torchmetrics to compute mAP (mean average precision) and mAR (mean average recall) metrics and will wrap it to compute_metrics function in order to use in [Trainer] for evaluation.

Intermediate format of boxes used for training is YOLO (normalized) but we will compute metrics for boxes in Pascal VOC (absolute) format in order to correctly handle box areas. Let's define a function that converts bounding boxes to Pascal VOC format:

Python
>>> from transformers.image_transforms import center_to_corners_format\n\n>>> def convert_bbox_yolo_to_pascal(boxes, image_size):\n...     \"\"\"\n...     Convert bounding boxes from YOLO format (x_center, y_center, width, height) in range [0, 1]\n...     to Pascal VOC format (x_min, y_min, x_max, y_max) in absolute coordinates.\n\n...     Args:\n...         boxes (torch.Tensor): Bounding boxes in YOLO format\n...         image_size (Tuple[int, int]): Image size in format (height, width)\n\n...     Returns:\n...         torch.Tensor: Bounding boxes in Pascal VOC format (x_min, y_min, x_max, y_max)\n...     \"\"\"\n...     # convert center to corners format\n...     boxes = center_to_corners_format(boxes)\n\n...     # convert to absolute coordinates\n...     height, width = image_size\n...     boxes = boxes * torch.tensor([[width, height, width, height]])\n\n...     return boxes\n

Then, in compute_metrics function we collect predicted and target bounding boxes, scores and labels from evaluation loop results and pass it to the scoring function.

Python
>>> import numpy as np\n>>> from dataclasses import dataclass\n>>> from torchmetrics.detection.mean_ap import MeanAveragePrecision\n\n\n>>> @dataclass\n>>> class ModelOutput:\n...     logits: torch.Tensor\n...     pred_boxes: torch.Tensor\n\n\n>>> @torch.no_grad()\n>>> def compute_metrics(evaluation_results, image_processor, threshold=0.0, id2label=None):\n...     \"\"\"\n...     Compute mean average mAP, mAR and their variants for the object detection task.\n\n...     Args:\n...         evaluation_results (EvalPrediction): Predictions and targets from evaluation.\n...         threshold (float, optional): Threshold to filter predicted boxes by confidence. Defaults to 0.0.\n...         id2label (Optional[dict], optional): Mapping from class id to class name. Defaults to None.\n\n...     Returns:\n...         Mapping[str, float]: Metrics in a form of dictionary {<metric_name>: <metric_value>}\n...     \"\"\"\n\n...     predictions, targets = evaluation_results.predictions, evaluation_results.label_ids\n\n...     # For metric computation we need to provide:\n...     #  - targets in a form of list of dictionaries with keys \"boxes\", \"labels\"\n...     #  - predictions in a form of list of dictionaries with keys \"boxes\", \"scores\", \"labels\"\n\n...     image_sizes = []\n...     post_processed_targets = []\n...     post_processed_predictions = []\n\n...     # Collect targets in the required format for metric computation\n...     for batch in targets:\n...         # collect image sizes, we will need them for predictions post processing\n...         batch_image_sizes = torch.tensor(np.array([x[\"orig_size\"] for x in batch]))\n...         image_sizes.append(batch_image_sizes)\n...         # collect targets in the required format for metric computation\n...         # boxes were converted to YOLO format needed for model training\n...         # here we will convert them to Pascal VOC format (x_min, y_min, x_max, y_max)\n...         for image_target in batch:\n...             boxes = torch.tensor(image_target[\"boxes\"])\n...             boxes = convert_bbox_yolo_to_pascal(boxes, image_target[\"orig_size\"])\n...             labels = torch.tensor(image_target[\"class_labels\"])\n...             post_processed_targets.append({\"boxes\": boxes, \"labels\": labels})\n\n...     # Collect predictions in the required format for metric computation,\n...     # model produce boxes in YOLO format, then image_processor convert them to Pascal VOC format\n...     for batch, target_sizes in zip(predictions, image_sizes):\n...         batch_logits, batch_boxes = batch[1], batch[2]\n...         output = ModelOutput(logits=torch.tensor(batch_logits), pred_boxes=torch.tensor(batch_boxes))\n...         post_processed_output = image_processor.post_process_object_detection(\n...             output, threshold=threshold, target_sizes=target_sizes\n...         )\n...         post_processed_predictions.extend(post_processed_output)\n\n...     # Compute metrics\n...     metric = MeanAveragePrecision(box_format=\"xyxy\", class_metrics=True)\n...     metric.update(post_processed_predictions, post_processed_targets)\n...     metrics = metric.compute()\n\n...     # Replace list of per class metrics with separate metric for each class\n...     classes = metrics.pop(\"classes\")\n...     map_per_class = metrics.pop(\"map_per_class\")\n...     mar_100_per_class = metrics.pop(\"mar_100_per_class\")\n...     for class_id, class_map, class_mar in zip(classes, map_per_class, mar_100_per_class):\n...         class_name = id2label[class_id.item()] if id2label is not None else class_id.item()\n...         metrics[f\"map_{class_name}\"] = class_map\n...         metrics[f\"mar_100_{class_name}\"] = class_mar\n\n...     metrics = {k: round(v.item(), 4) for k, v in metrics.items()}\n\n...     return metrics\n\n\n>>> eval_compute_metrics_fn = partial(\n...     compute_metrics, image_processor=image_processor, id2label=id2label, threshold=0.0\n... )\n
"},{"location":"integrations/huggingface/object_detection/#training-the-detection-model","title":"Training the detection model","text":"

You have done most of the heavy lifting in the previous sections, so now you are ready to train your model! The images in this dataset are still quite large, even after resizing. This means that finetuning this model will require at least one GPU.

Training involves the following steps: 1. Load the model with [AutoModelForObjectDetection] using the same checkpoint as in the preprocessing. 2. Define your training hyperparameters in [TrainingArguments]. 3. Pass the training arguments to [Trainer] along with the model, dataset, image processor, and data collator. 4. Call [~Trainer.train] to finetune your model.

When loading the model from the same checkpoint that you used for the preprocessing, remember to pass the label2id and id2label maps that you created earlier from the dataset's metadata. Additionally, we specify ignore_mismatched_sizes=True to replace the existing classification head with a new one.

Python
>>> from transformers import AutoModelForObjectDetection\n\n>>> model = AutoModelForObjectDetection.from_pretrained(\n...     MODEL_NAME,\n...     id2label=id2label,\n...     label2id=label2id,\n...     ignore_mismatched_sizes=True,\n... )\n

In the [TrainingArguments] use output_dir to specify where to save your model, then configure hyperparameters as you see fit. For num_train_epochs=30 training will take about 35 minutes in Google Colab T4 GPU, increase the number of epoch to get better results.

Important notes: - Do not remove unused columns because this will drop the image column. Without the image column, you can't create pixel_values. For this reason, set remove_unused_columns to False. - Set eval_do_concat_batches=False to get proper evaluation results. Images have different number of target boxes, if batches are concatenated we will not be able to determine which boxes belongs to particular image.

If you wish to share your model by pushing to the Hub, set push_to_hub to True (you must be signed in to Hugging Face to upload your model).

Python
>>> from transformers import TrainingArguments\n\n>>> training_args = TrainingArguments(\n...     output_dir=\"detr_finetuned_cppe5\",\n...     num_train_epochs=30,\n...     fp16=False,\n...     per_device_train_batch_size=8,\n...     dataloader_num_workers=4,\n...     learning_rate=5e-5,\n...     lr_scheduler_type=\"cosine\",\n...     weight_decay=1e-4,\n...     max_grad_norm=0.01,\n...     metric_for_best_model=\"eval_map\",\n...     greater_is_better=True,\n...     load_best_model_at_end=True,\n...     eval_strategy=\"epoch\",\n...     save_strategy=\"epoch\",\n...     save_total_limit=2,\n...     remove_unused_columns=False,\n...     eval_do_concat_batches=False,\n...     push_to_hub=True,\n... )\n

Finally, bring everything together, and call [~transformers.Trainer.train]:

Python
>>> from transformers import Trainer\n\n>>> trainer = Trainer(\n...     model=model,\n...     args=training_args,\n...     train_dataset=cppe5[\"train\"],\n...     eval_dataset=cppe5[\"validation\"],\n...     processing_class=image_processor,\n...     data_collator=collate_fn,\n...     compute_metrics=eval_compute_metrics_fn,\n... )\n\n>>> trainer.train()\n
[3210/3210 26:07, Epoch 30/30] Epoch Training Loss Validation Loss Map Map 50 Map 75 Map Small Map Medium Map Large Mar 1 Mar 10 Mar 100 Mar Small Mar Medium Mar Large Map Coverall Mar 100 Coverall Map Face Shield Mar 100 Face Shield Map Gloves Mar 100 Gloves Map Goggles Mar 100 Goggles Map Mask Mar 100 Mask 1 No log 2.629903 0.008900 0.023200 0.006500 0.001300 0.002800 0.020500 0.021500 0.070400 0.101400 0.007600 0.106200 0.096100 0.036700 0.232000 0.000300 0.019000 0.003900 0.125400 0.000100 0.003100 0.003500 0.127600 2 No log 3.479864 0.014800 0.034600 0.010800 0.008600 0.011700 0.012500 0.041100 0.098700 0.130000 0.056000 0.062200 0.111900 0.053500 0.447300 0.010600 0.100000 0.000200 0.022800 0.000100 0.015400 0.009700 0.064400 3 No log 2.107622 0.041700 0.094000 0.034300 0.024100 0.026400 0.047400 0.091500 0.182800 0.225800 0.087200 0.199400 0.210600 0.150900 0.571200 0.017300 0.101300 0.007300 0.180400 0.002100 0.026200 0.031000 0.250200 4 No log 2.031242 0.055900 0.120600 0.046900 0.013800 0.038100 0.090300 0.105900 0.225600 0.266100 0.130200 0.228100 0.330000 0.191000 0.572100 0.010600 0.157000 0.014600 0.235300 0.001700 0.052300 0.061800 0.313800 5 3.889400 1.883433 0.089700 0.201800 0.067300 0.022800 0.065300 0.129500 0.136000 0.272200 0.303700 0.112900 0.312500 0.424600 0.300200 0.585100 0.032700 0.202500 0.031300 0.271000 0.008700 0.126200 0.075500 0.333800 6 3.889400 1.807503 0.118500 0.270900 0.090200 0.034900 0.076700 0.152500 0.146100 0.297800 0.325400 0.171700 0.283700 0.545900 0.396900 0.554500 0.043000 0.262000 0.054500 0.271900 0.020300 0.230800 0.077600 0.308000 7 3.889400 1.716169 0.143500 0.307700 0.123200 0.045800 0.097800 0.258300 0.165300 0.327700 0.352600 0.140900 0.336700 0.599400 0.442900 0.620700 0.069400 0.301300 0.081600 0.292000 0.011000 0.230800 0.112700 0.318200 8 3.889400 1.679014 0.153000 0.355800 0.127900 0.038700 0.115600 0.291600 0.176000 0.322500 0.349700 0.135600 0.326100 0.643700 0.431700 0.582900 0.069800 0.265800 0.088600 0.274600 0.028300 0.280000 0.146700 0.345300 9 3.889400 1.618239 0.172100 0.375300 0.137600 0.046100 0.141700 0.308500 0.194000 0.356200 0.386200 0.162400 0.359200 0.677700 0.469800 0.623900 0.102100 0.317700 0.099100 0.290200 0.029300 0.335400 0.160200 0.364000 10 1.599700 1.572512 0.179500 0.400400 0.147200 0.056500 0.141700 0.316700 0.213100 0.357600 0.381300 0.197900 0.344300 0.638500 0.466900 0.623900 0.101300 0.311400 0.104700 0.279500 0.051600 0.338500 0.173000 0.353300 11 1.599700 1.528889 0.192200 0.415000 0.160800 0.053700 0.150500 0.378000 0.211500 0.371700 0.397800 0.204900 0.374600 0.684800 0.491900 0.632400 0.131200 0.346800 0.122000 0.300900 0.038400 0.344600 0.177500 0.364400 12 1.599700 1.517532 0.198300 0.429800 0.159800 0.066400 0.162900 0.383300 0.220700 0.382100 0.405400 0.214800 0.383200 0.672900 0.469000 0.610400 0.167800 0.379700 0.119700 0.307100 0.038100 0.335400 0.196800 0.394200 13 1.599700 1.488849 0.209800 0.452300 0.172300 0.094900 0.171100 0.437800 0.222000 0.379800 0.411500 0.203800 0.397300 0.707500 0.470700 0.620700 0.186900 0.407600 0.124200 0.306700 0.059300 0.355400 0.207700 0.367100 14 1.599700 1.482210 0.228900 0.482600 0.187800 0.083600 0.191800 0.444100 0.225900 0.376900 0.407400 0.182500 0.384800 0.700600 0.512100 0.640100 0.175000 0.363300 0.144300 0.300000 0.083100 0.363100 0.229900 0.370700 15 1.326800 1.475198 0.216300 0.455600 0.174900 0.088500 0.183500 0.424400 0.226900 0.373400 0.404300 0.199200 0.396400 0.677800 0.496300 0.633800 0.166300 0.392400 0.128900 0.312900 0.085200 0.312300 0.205000 0.370200 16 1.326800 1.459697 0.233200 0.504200 0.192200 0.096000 0.202000 0.430800 0.239100 0.382400 0.412600 0.219500 0.403100 0.670400 0.485200 0.625200 0.196500 0.410100 0.135700 0.299600 0.123100 0.356900 0.225300 0.371100 17 1.326800 1.407340 0.243400 0.511900 0.204500 0.121000 0.215700 0.468000 0.246200 0.394600 0.424200 0.225900 0.416100 0.705200 0.494900 0.638300 0.224900 0.430400 0.157200 0.317900 0.115700 0.369200 0.224200 0.365300 18 1.326800 1.419522 0.245100 0.521500 0.210000 0.116100 0.211500 0.489900 0.255400 0.391600 0.419700 0.198800 0.421200 0.701400 0.501800 0.634200 0.226700 0.410100 0.154400 0.321400 0.105900 0.352300 0.236700 0.380400 19 1.158600 1.398764 0.253600 0.519200 0.213600 0.135200 0.207700 0.491900 0.257300 0.397300 0.428000 0.241400 0.401800 0.703500 0.509700 0.631100 0.236700 0.441800 0.155900 0.330800 0.128100 0.352300 0.237500 0.384000 20 1.158600 1.390591 0.248800 0.520200 0.216600 0.127500 0.211400 0.471900 0.258300 0.407000 0.429100 0.240300 0.407600 0.708500 0.505800 0.623400 0.235500 0.431600 0.150000 0.325000 0.125700 0.375400 0.227200 0.390200 21 1.158600 1.360608 0.262700 0.544800 0.222100 0.134700 0.230000 0.487500 0.269500 0.413300 0.436300 0.236200 0.419100 0.709300 0.514100 0.637400 0.257200 0.450600 0.165100 0.338400 0.139400 0.372300 0.237700 0.382700 22 1.158600 1.368296 0.262800 0.542400 0.236400 0.137400 0.228100 0.498500 0.266500 0.409000 0.433000 0.239900 0.418500 0.697500 0.520500 0.641000 0.257500 0.455700 0.162600 0.334800 0.140200 0.353800 0.233200 0.379600 23 1.158600 1.368176 0.264800 0.541100 0.233100 0.138200 0.223900 0.498700 0.272300 0.407400 0.434400 0.233100 0.418300 0.702000 0.524400 0.642300 0.262300 0.444300 0.159700 0.335300 0.140500 0.366200 0.236900 0.384000 24 1.049700 1.355271 0.269700 0.549200 0.239100 0.134700 0.229900 0.519200 0.274800 0.412700 0.437600 0.245400 0.417200 0.711200 0.523200 0.644100 0.272100 0.440500 0.166700 0.341500 0.137700 0.373800 0.249000 0.388000 25 1.049700 1.355180 0.272500 0.547900 0.243800 0.149700 0.229900 0.523100 0.272500 0.415700 0.442200 0.256200 0.420200 0.705800 0.523900 0.639600 0.271700 0.451900 0.166300 0.346900 0.153700 0.383100 0.247000 0.389300 26 1.049700 1.349337 0.275600 0.556300 0.246400 0.146700 0.234800 0.516300 0.274200 0.418300 0.440900 0.248700 0.418900 0.705800 0.523200 0.636500 0.274700 0.440500 0.172400 0.349100 0.155600 0.384600 0.252300 0.393800 27 1.049700 1.350782 0.275200 0.548700 0.246800 0.147300 0.236400 0.527200 0.280100 0.416200 0.442600 0.253400 0.424000 0.710300 0.526600 0.640100 0.273200 0.445600 0.167000 0.346900 0.160100 0.387700 0.249200 0.392900 28 1.049700 1.346533 0.277000 0.552800 0.252900 0.147400 0.240000 0.527600 0.280900 0.420900 0.444100 0.255500 0.424500 0.711200 0.530200 0.646800 0.277400 0.441800 0.170900 0.346900 0.156600 0.389200 0.249600 0.396000 29 0.993700 1.346575 0.277100 0.554800 0.252900 0.148400 0.239700 0.523600 0.278400 0.420000 0.443300 0.256300 0.424000 0.705600 0.529600 0.647300 0.273900 0.439200 0.174300 0.348700 0.157600 0.386200 0.250100 0.395100 30 0.993700 1.346446 0.277400 0.554700 0.252700 0.147900 0.240800 0.523600 0.278800 0.420400 0.443300 0.256100 0.424200 0.705500 0.530100 0.646800 0.275600 0.440500 0.174500 0.348700 0.157300 0.386200 0.249200 0.394200

If you have set `push_to_hub` to `True` in the `training_args`, the training checkpoints are pushed to the Hugging Face Hub. Upon training completion, push the final model to the Hub as well by calling the [`~transformers.Trainer.push_to_hub`] method. Python

>>> trainer.push_to_hub()\n
## Evaluate Python
>>> from pprint import pprint\n\n>>> metrics = trainer.evaluate(eval_dataset=cppe5[\"test\"], metric_key_prefix=\"test\")\n>>> pprint(metrics)\n{'epoch': 30.0,\n  'test_loss': 1.0877351760864258,\n  'test_map': 0.4116,\n  'test_map_50': 0.741,\n  'test_map_75': 0.3663,\n  'test_map_Coverall': 0.5937,\n  'test_map_Face_Shield': 0.5863,\n  'test_map_Gloves': 0.3416,\n  'test_map_Goggles': 0.1468,\n  'test_map_Mask': 0.3894,\n  'test_map_large': 0.5637,\n  'test_map_medium': 0.3257,\n  'test_map_small': 0.3589,\n  'test_mar_1': 0.323,\n  'test_mar_10': 0.5237,\n  'test_mar_100': 0.5587,\n  'test_mar_100_Coverall': 0.6756,\n  'test_mar_100_Face_Shield': 0.7294,\n  'test_mar_100_Gloves': 0.4721,\n  'test_mar_100_Goggles': 0.4125,\n  'test_mar_100_Mask': 0.5038,\n  'test_mar_large': 0.7283,\n  'test_mar_medium': 0.4901,\n  'test_mar_small': 0.4469,\n  'test_runtime': 1.6526,\n  'test_samples_per_second': 17.548,\n  'test_steps_per_second': 2.42}\n
These results can be further improved by adjusting the hyperparameters in [`TrainingArguments`]. Give it a go! ## Inference Now that you have finetuned a model, evaluated it, and uploaded it to the Hugging Face Hub, you can use it for inference. Python
>>> import torch\n>>> import requests\n\n>>> from PIL import Image, ImageDraw\n>>> from transformers import AutoImageProcessor, AutoModelForObjectDetection\n\n>>> url = \"https://images.pexels.com/photos/8413299/pexels-photo-8413299.jpeg?auto=compress&cs=tinysrgb&w=630&h=375&dpr=2\"\n>>> image = Image.open(requests.get(url, stream=True).raw)\n
Load model and image processor from the Hugging Face Hub (skip to use already trained in this session): Python
>>> from accelerate.test_utils.testing import get_backend\n# automatically detects the underlying device type (CUDA, CPU, XPU, MPS, etc.)\n>>> device, _, _ = get_backend()\n>>> model_repo = \"qubvel-hf/detr_finetuned_cppe5\"\n\n>>> image_processor = AutoImageProcessor.from_pretrained(model_repo)\n>>> model = AutoModelForObjectDetection.from_pretrained(model_repo)\n>>> model = model.to(device)\n
And detect bounding boxes: Python
>>> with torch.no_grad():\n...     inputs = image_processor(images=[image], return_tensors=\"pt\")\n...     outputs = model(**inputs.to(device))\n...     target_sizes = torch.tensor([[image.size[1], image.size[0]]])\n...     results = image_processor.post_process_object_detection(outputs, threshold=0.3, target_sizes=target_sizes)[0]\n\n>>> for score, label, box in zip(results[\"scores\"], results[\"labels\"], results[\"boxes\"]):\n...     box = [round(i, 2) for i in box.tolist()]\n...     print(\n...         f\"Detected {model.config.id2label[label.item()]} with confidence \"\n...         f\"{round(score.item(), 3)} at location {box}\"\n...     )\nDetected Gloves with confidence 0.683 at location [244.58, 124.33, 300.35, 185.13]\nDetected Mask with confidence 0.517 at location [143.73, 64.58, 219.57, 125.89]\nDetected Gloves with confidence 0.425 at location [179.15, 155.57, 262.4, 226.35]\nDetected Coverall with confidence 0.407 at location [307.13, -1.18, 477.82, 318.06]\nDetected Coverall with confidence 0.391 at location [68.61, 126.66, 309.03, 318.89]\n
Let's plot the result: Python
>>> draw = ImageDraw.Draw(image)\n\n>>> for score, label, box in zip(results[\"scores\"], results[\"labels\"], results[\"boxes\"]):\n...     box = [round(i, 2) for i in box.tolist()]\n...     x, y, x2, y2 = tuple(box)\n...     draw.rectangle((x, y, x2, y2), outline=\"red\", width=1)\n...     draw.text((x, y), model.config.id2label[label.item()], fill=\"white\")\n\n>>> image\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/","title":"How to Train RT-DETR on Custom Dataset with Roboflow, HuggingFace and Albumentations","text":""},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#how-to-train-rt-detr-on-custom-dataset","title":"How to Train RT-DETR on Custom Dataset","text":"

RT-DETR, short for \"Real-Time DEtection TRansformer\", is a computer vision model developed by Peking University and Baidu. In their paper, \"DETRs Beat YOLOs on Real-time Object Detection\" the authors claim that RT-DETR can outperform YOLO models in object detection, both in terms of speed and accuracy. The model has been released under the Apache 2.0 license, making it a great option, especially for enterprise projects.

Recently, RT-DETR was added to the transformers library, significantly simplifying its fine-tuning process. In this tutorial, we will show you how to train RT-DETR on a custom dataset.

"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#setup","title":"Setup","text":""},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#configure-your-api-keys","title":"Configure your API keys","text":"

To fine-tune RT-DETR, you need to provide your HuggingFace Token and Roboflow API key. Follow these steps:

  • Open your HuggingFace Settings page. Click Access Tokens then New Token to generate new token.
  • Go to your Roboflow Settings page. Click Copy. This will place your private key in the clipboard.
  • In Colab, go to the left pane and click on Secrets (\ud83d\udd11).
    • Store HuggingFace Access Token under the name HF_TOKEN.
    • Store Roboflow API Key under the name ROBOFLOW_API_KEY.
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#select-the-runtime","title":"Select the runtime","text":"

Let's make sure that we have access to GPU. We can use nvidia-smi command to do that. In case of any problems navigate to Edit -> Notebook settings -> Hardware accelerator, set it to L4 GPU, and then click Save.

Python
!nvidia-smi\n
Thu Jul 11 09:20:53 2024       \n+---------------------------------------------------------------------------------------+\n| NVIDIA-SMI 535.104.05             Driver Version: 535.104.05   CUDA Version: 12.2     |\n|-----------------------------------------+----------------------+----------------------+\n| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |\n| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |\n|                                         |                      |               MIG M. |\n|=========================================+======================+======================|\n|   0  Tesla T4                       Off | 00000000:00:04.0 Off |                    0 |\n| N/A   65C    P8              11W /  70W |      0MiB / 15360MiB |      0%      Default |\n|                                         |                      |                  N/A |\n+-----------------------------------------+----------------------+----------------------+\n\n+---------------------------------------------------------------------------------------+\n| Processes:                                                                            |\n|  GPU   GI   CI        PID   Type   Process name                            GPU Memory |\n|        ID   ID                                                             Usage      |\n|=======================================================================================|\n|  No running processes found                                                           |\n+---------------------------------------------------------------------------------------+\n

NOTE: To make it easier for us to manage datasets, images and models we create a HOME constant.

Python
import os\nHOME = os.getcwd()\nprint(\"HOME:\", HOME)\n
HOME: /content\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#install-dependencies","title":"Install dependencies","text":"Python
!pip install -q git+https://github.com/huggingface/transformers.git\n!pip install -q git+https://github.com/roboflow/supervision.git\n!pip install -q accelerate\n!pip install -q roboflow\n!pip install -q torchmetrics\n!pip install -q \"albumentations>=1.4.5\"\n
  Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n  Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n  Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Building wheel for transformers (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n  Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n  Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n  Building wheel for supervision (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m314.1/314.1 kB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m21.3/21.3 MB\u001b[0m \u001b[31m71.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m76.2/76.2 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m178.7/178.7 kB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m54.5/54.5 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m868.8/868.8 kB\u001b[0m \u001b[31m6.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m165.3/165.3 kB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m14.9/14.9 MB\u001b[0m \u001b[31m83.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m13.4/13.4 MB\u001b[0m \u001b[31m92.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K     \u001b[90m\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u001b[0m \u001b[32m313.5/313.5 kB\u001b[0m \u001b[31m34.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25h\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#imports","title":"Imports","text":"Python
import torch\nimport requests\n\nimport numpy as np\nimport supervision as sv\nimport albumentations as A\n\nfrom PIL import Image\nfrom pprint import pprint\nfrom roboflow import Roboflow\nfrom dataclasses import dataclass, replace\nfrom google.colab import userdata\nfrom torch.utils.data import Dataset\nfrom transformers import (\n    AutoImageProcessor,\n    AutoModelForObjectDetection,\n    TrainingArguments,\n    Trainer\n)\nfrom torchmetrics.detection.mean_ap import MeanAveragePrecision\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#inference-with-pre-trained-rt-detr-model","title":"Inference with pre-trained RT-DETR model","text":"Python
# @title Load model\n\nCHECKPOINT = \"PekingU/rtdetr_r50vd_coco_o365\"\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = AutoModelForObjectDetection.from_pretrained(CHECKPOINT).to(DEVICE)\nprocessor = AutoImageProcessor.from_pretrained(CHECKPOINT)\n
config.json:   0%|          | 0.00/5.11k [00:00<?, ?B/s]\n\n\n\nmodel.safetensors:   0%|          | 0.00/172M [00:00<?, ?B/s]\n\n\n\npreprocessor_config.json:   0%|          | 0.00/841 [00:00<?, ?B/s]\n
Python
# @title Run inference\n\nURL = \"https://media.roboflow.com/notebooks/examples/dog.jpeg\"\n\nimage = Image.open(requests.get(URL, stream=True).raw)\ninputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\nwith torch.no_grad():\n    outputs = model(**inputs)\n\nw, h = image.size\nresults = processor.post_process_object_detection(\n    outputs, target_sizes=[(h, w)], threshold=0.3)\n
Python
# @title Display result with NMS\n\ndetections = sv.Detections.from_transformers(results[0])\nlabels = [\n    model.config.id2label[class_id]\n    for class_id\n    in detections.class_id\n]\n\nannotated_image = image.copy()\nannotated_image = sv.BoundingBoxAnnotator().annotate(annotated_image, detections)\nannotated_image = sv.LabelAnnotator().annotate(annotated_image, detections, labels=labels)\nannotated_image.thumbnail((600, 600))\nannotated_image\n
Python
# @title Display result with NMS\n\ndetections = sv.Detections.from_transformers(results[0]).with_nms(threshold=0.1)\nlabels = [\n    model.config.id2label[class_id]\n    for class_id\n    in detections.class_id\n]\n\nannotated_image = image.copy()\nannotated_image = sv.BoundingBoxAnnotator().annotate(annotated_image, detections)\nannotated_image = sv.LabelAnnotator().annotate(annotated_image, detections, labels=labels)\nannotated_image.thumbnail((600, 600))\nannotated_image\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#fine-tune-rt-detr-on-custom-dataset","title":"Fine-tune RT-DETR on custom dataset","text":"Python
# @title Download dataset from Roboflow Universe\n\nROBOFLOW_API_KEY = userdata.get('ROBOFLOW_API_KEY')\nrf = Roboflow(api_key=ROBOFLOW_API_KEY)\n\nproject = rf.workspace(\"roboflow-jvuqo\").project(\"poker-cards-fmjio\")\nversion = project.version(4)\ndataset = version.download(\"coco\")\n
loading Roboflow workspace...\nloading Roboflow project...\n\n\nDownloading Dataset Version Zip in poker-cards-4 to coco:: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 39123/39123 [00:01<00:00, 27288.54it/s]\n\n\n\n\n\nExtracting Dataset Version Zip to poker-cards-4 in coco:: 100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 907/907 [00:00<00:00, 2984.59it/s]\n
Python
ds_train = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/train\",\n    annotations_path=f\"{dataset.location}/train/_annotations.coco.json\",\n)\nds_valid = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/valid\",\n    annotations_path=f\"{dataset.location}/valid/_annotations.coco.json\",\n)\nds_test = sv.DetectionDataset.from_coco(\n    images_directory_path=f\"{dataset.location}/test\",\n    annotations_path=f\"{dataset.location}/test/_annotations.coco.json\",\n)\n\nprint(f\"Number of training images: {len(ds_train)}\")\nprint(f\"Number of validation images: {len(ds_valid)}\")\nprint(f\"Number of test images: {len(ds_test)}\")\n
Number of training images: 811\nNumber of validation images: 44\nNumber of test images: 44\n
Python
# @title Display dataset sample\n\nGRID_SIZE = 5\n\ndef annotate(image, annotations, classes):\n    labels = [\n        classes[class_id]\n        for class_id\n        in annotations.class_id\n    ]\n\n    bounding_box_annotator = sv.BoundingBoxAnnotator()\n    label_annotator = sv.LabelAnnotator(text_scale=1, text_thickness=2)\n\n    annotated_image = image.copy()\n    annotated_image = bounding_box_annotator.annotate(annotated_image, annotations)\n    annotated_image = label_annotator.annotate(annotated_image, annotations, labels=labels)\n    return annotated_image\n\nannotated_images = []\nfor i in range(GRID_SIZE * GRID_SIZE):\n    _, image, annotations = ds_train[i]\n    annotated_image = annotate(image, annotations, ds_train.classes)\n    annotated_images.append(annotated_image)\n\ngrid = sv.create_tiles(\n    annotated_images,\n    grid_size=(GRID_SIZE, GRID_SIZE),\n    single_tile_size=(400, 400),\n    tile_padding_color=sv.Color.WHITE,\n    tile_margin_color=sv.Color.WHITE\n)\nsv.plot_image(grid, size=(10, 10))\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#preprocess-the-data","title":"Preprocess the data","text":"

To finetune a model, you must preprocess the data you plan to use to match precisely the approach used for the pre-trained model. AutoImageProcessor takes care of processing image data to create pixel_values, pixel_mask, and labels that a DETR model can train with. The image processor has some attributes that you won't have to worry about:

  • image_mean = [0.485, 0.456, 0.406 ]
  • image_std = [0.229, 0.224, 0.225]

These are the mean and standard deviation used to normalize images during the model pre-training. These values are crucial to replicate when doing inference or finetuning a pre-trained image model.

Instantiate the image processor from the same checkpoint as the model you want to finetune.

Python
IMAGE_SIZE = 480\n\nprocessor = AutoImageProcessor.from_pretrained(\n    CHECKPOINT,\n    do_resize=True,\n    size={\"width\": IMAGE_SIZE, \"height\": IMAGE_SIZE},\n)\n

Before passing the images to the processor, apply two preprocessing transformations to the dataset:

  • Augmenting images
  • Reformatting annotations to meet RT-DETR expectations

First, to make sure the model does not overfit on the training data, you can apply image augmentation with any data augmentation library. Here we use Albumentations. This library ensures that transformations affect the image and update the bounding boxes accordingly.

Python
train_augmentation_and_transform = A.Compose(\n    [\n        A.Perspective(p=0.1),\n        A.HorizontalFlip(p=0.5),\n        A.RandomBrightnessContrast(p=0.5),\n        A.HueSaturationValue(p=0.1),\n    ],\n    bbox_params=A.BboxParams(\n        format=\"pascal_voc\",\n        label_fields=[\"category\"],\n        clip=True,\n        min_area=25\n    ),\n)\n\nvalid_transform = A.Compose(\n    [A.NoOp()],\n    bbox_params=A.BboxParams(\n        format=\"pascal_voc\",\n        label_fields=[\"category\"],\n        clip=True,\n        min_area=1\n    ),\n)\n
Python
# @title Visualize some augmented images\n\nIMAGE_COUNT = 5\n\nfor i in range(IMAGE_COUNT):\n    _, image, annotations = ds_train[i]\n\n    output = train_augmentation_and_transform(\n        image=image,\n        bboxes=annotations.xyxy,\n        category=annotations.class_id\n    )\n\n    augmented_image = output[\"image\"]\n    augmented_annotations = replace(\n        annotations,\n        xyxy=np.array(output[\"bboxes\"]),\n        class_id=np.array(output[\"category\"])\n    )\n\n    annotated_images = [\n        annotate(image, annotations, ds_train.classes),\n        annotate(augmented_image, augmented_annotations, ds_train.classes)\n    ]\n    grid = sv.create_tiles(\n        annotated_images,\n        titles=['original', 'augmented'],\n        titles_scale=0.5,\n        single_tile_size=(400, 400),\n        tile_padding_color=sv.Color.WHITE,\n        tile_margin_color=sv.Color.WHITE\n    )\n    sv.plot_image(grid, size=(6, 6))\n

The processor expects the annotations to be in the following format: {'image_id': int, 'annotations': List[Dict]}, where each dictionary is a COCO object annotation. Let's add a function to reformat annotations for a single example:

Python
class PyTorchDetectionDataset(Dataset):\n    def __init__(self, dataset: sv.DetectionDataset, processor, transform: A.Compose = None):\n        self.dataset = dataset\n        self.processor = processor\n        self.transform = transform\n\n    @staticmethod\n    def annotations_as_coco(image_id, categories, boxes):\n        annotations = []\n        for category, bbox in zip(categories, boxes):\n            x1, y1, x2, y2 = bbox\n            formatted_annotation = {\n                \"image_id\": image_id,\n                \"category_id\": category,\n                \"bbox\": [x1, y1, x2 - x1, y2 - y1],\n                \"iscrowd\": 0,\n                \"area\": (x2 - x1) * (y2 - y1),\n            }\n            annotations.append(formatted_annotation)\n\n        return {\n            \"image_id\": image_id,\n            \"annotations\": annotations,\n        }\n\n    def __len__(self):\n        return len(self.dataset)\n\n    def __getitem__(self, idx):\n        _, image, annotations = self.dataset[idx]\n\n        # Convert image to RGB numpy array\n        image = image[:, :, ::-1]\n        boxes = annotations.xyxy\n        categories = annotations.class_id\n\n        if self.transform:\n            transformed = self.transform(\n                image=image,\n                bboxes=boxes,\n                category=categories\n            )\n            image = transformed[\"image\"]\n            boxes = transformed[\"bboxes\"]\n            categories = transformed[\"category\"]\n\n\n        formatted_annotations = self.annotations_as_coco(\n            image_id=idx, categories=categories, boxes=boxes)\n        result = self.processor(\n            images=image, annotations=formatted_annotations, return_tensors=\"pt\")\n\n        # Image processor expands batch dimension, lets squeeze it\n        result = {k: v[0] for k, v in result.items()}\n\n        return result\n

Now you can combine the image and annotation transformations to use on a batch of examples:

Python
pytorch_dataset_train = PyTorchDetectionDataset(\n    ds_train, processor, transform=train_augmentation_and_transform)\npytorch_dataset_valid = PyTorchDetectionDataset(\n    ds_valid, processor, transform=valid_transform)\npytorch_dataset_test = PyTorchDetectionDataset(\n    ds_test, processor, transform=valid_transform)\n\npytorch_dataset_train[15]\n
{'pixel_values': tensor([[[0.0745, 0.0745, 0.0745,  ..., 0.2431, 0.2471, 0.2471],\n          [0.0745, 0.0745, 0.0745,  ..., 0.2510, 0.2549, 0.2549],\n          [0.0667, 0.0706, 0.0706,  ..., 0.2588, 0.2588, 0.2588],\n          ...,\n          [0.0118, 0.0118, 0.0118,  ..., 0.0510, 0.0549, 0.0510],\n          [0.0157, 0.0196, 0.0235,  ..., 0.0549, 0.0627, 0.0549],\n          [0.0235, 0.0275, 0.0314,  ..., 0.0549, 0.0627, 0.0549]],\n\n         [[0.0549, 0.0549, 0.0549,  ..., 0.3137, 0.3176, 0.3176],\n          [0.0549, 0.0549, 0.0549,  ..., 0.3216, 0.3255, 0.3255],\n          [0.0471, 0.0510, 0.0510,  ..., 0.3294, 0.3294, 0.3294],\n          ...,\n          [0.0000, 0.0000, 0.0000,  ..., 0.0353, 0.0392, 0.0353],\n          [0.0000, 0.0000, 0.0039,  ..., 0.0392, 0.0471, 0.0392],\n          [0.0000, 0.0039, 0.0078,  ..., 0.0392, 0.0471, 0.0392]],\n\n         [[0.0431, 0.0431, 0.0431,  ..., 0.3922, 0.3961, 0.3961],\n          [0.0431, 0.0431, 0.0431,  ..., 0.4000, 0.4039, 0.4039],\n          [0.0353, 0.0392, 0.0392,  ..., 0.4078, 0.4078, 0.4078],\n          ...,\n          [0.0000, 0.0000, 0.0000,  ..., 0.0314, 0.0353, 0.0314],\n          [0.0000, 0.0000, 0.0039,  ..., 0.0353, 0.0431, 0.0353],\n          [0.0000, 0.0039, 0.0078,  ..., 0.0353, 0.0431, 0.0353]]]),\n 'labels': {'size': tensor([480, 480]), 'image_id': tensor([15]), 'class_labels': tensor([36,  4, 44, 52, 48]), 'boxes': tensor([[0.7891, 0.4437, 0.2094, 0.3562],\n         [0.3984, 0.6484, 0.3187, 0.3906],\n         [0.5891, 0.4070, 0.2219, 0.3859],\n         [0.3484, 0.2812, 0.2625, 0.4094],\n         [0.1602, 0.5023, 0.2672, 0.4109]]), 'area': tensor([17185.5000, 28687.5000, 19729.1250, 24759.0000, 25297.3125]), 'iscrowd': tensor([0, 0, 0, 0, 0]), 'orig_size': tensor([640, 640])}}\n

You have successfully augmented the images and prepared their annotations. In the final step, create a custom collate_fn to batch images together.

Python
def collate_fn(batch):\n    data = {}\n    data[\"pixel_values\"] = torch.stack([x[\"pixel_values\"] for x in batch])\n    data[\"labels\"] = [x[\"labels\"] for x in batch]\n    return data\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#preparing-function-to-compute-map","title":"Preparing function to compute mAP","text":"Python
id2label = {id: label for id, label in enumerate(ds_train.classes)}\nlabel2id = {label: id for id, label in enumerate(ds_train.classes)}\n\n\n@dataclass\nclass ModelOutput:\n    logits: torch.Tensor\n    pred_boxes: torch.Tensor\n\n\nclass MAPEvaluator:\n\n    def __init__(self, image_processor, threshold=0.00, id2label=None):\n        self.image_processor = image_processor\n        self.threshold = threshold\n        self.id2label = id2label\n\n    def collect_image_sizes(self, targets):\n        \"\"\"Collect image sizes across the dataset as list of tensors with shape [batch_size, 2].\"\"\"\n        image_sizes = []\n        for batch in targets:\n            batch_image_sizes = torch.tensor(np.array([x[\"size\"] for x in batch]))\n            image_sizes.append(batch_image_sizes)\n        return image_sizes\n\n    def collect_targets(self, targets, image_sizes):\n        post_processed_targets = []\n        for target_batch, image_size_batch in zip(targets, image_sizes):\n            for target, (height, width) in zip(target_batch, image_size_batch):\n                boxes = target[\"boxes\"]\n                boxes = sv.xcycwh_to_xyxy(boxes)\n                boxes = boxes * np.array([width, height, width, height])\n                boxes = torch.tensor(boxes)\n                labels = torch.tensor(target[\"class_labels\"])\n                post_processed_targets.append({\"boxes\": boxes, \"labels\": labels})\n        return post_processed_targets\n\n    def collect_predictions(self, predictions, image_sizes):\n        post_processed_predictions = []\n        for batch, target_sizes in zip(predictions, image_sizes):\n            batch_logits, batch_boxes = batch[1], batch[2]\n            output = ModelOutput(logits=torch.tensor(batch_logits), pred_boxes=torch.tensor(batch_boxes))\n            post_processed_output = self.image_processor.post_process_object_detection(\n                output, threshold=self.threshold, target_sizes=target_sizes\n            )\n            post_processed_predictions.extend(post_processed_output)\n        return post_processed_predictions\n\n    @torch.no_grad()\n    def __call__(self, evaluation_results):\n\n        predictions, targets = evaluation_results.predictions, evaluation_results.label_ids\n\n        image_sizes = self.collect_image_sizes(targets)\n        post_processed_targets = self.collect_targets(targets, image_sizes)\n        post_processed_predictions = self.collect_predictions(predictions, image_sizes)\n\n        evaluator = MeanAveragePrecision(box_format=\"xyxy\", class_metrics=True)\n        evaluator.warn_on_many_detections = False\n        evaluator.update(post_processed_predictions, post_processed_targets)\n\n        metrics = evaluator.compute()\n\n        # Replace list of per class metrics with separate metric for each class\n        classes = metrics.pop(\"classes\")\n        map_per_class = metrics.pop(\"map_per_class\")\n        mar_100_per_class = metrics.pop(\"mar_100_per_class\")\n        for class_id, class_map, class_mar in zip(classes, map_per_class, mar_100_per_class):\n            class_name = id2label[class_id.item()] if id2label is not None else class_id.item()\n            metrics[f\"map_{class_name}\"] = class_map\n            metrics[f\"mar_100_{class_name}\"] = class_mar\n\n        metrics = {k: round(v.item(), 4) for k, v in metrics.items()}\n\n        return metrics\n\neval_compute_metrics_fn = MAPEvaluator(image_processor=processor, threshold=0.01, id2label=id2label)\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#training-the-detection-model","title":"Training the detection model","text":"

You have done most of the heavy lifting in the previous sections, so now you are ready to train your model! The images in this dataset are still quite large, even after resizing. This means that finetuning this model will require at least one GPU.

Training involves the following steps:

  • Load the model with AutoModelForObjectDetection using the same checkpoint as in the preprocessing.
  • Define your training hyperparameters in TrainingArguments.
  • Pass the training arguments to Trainer along with the model, dataset, image processor, and data collator.
  • Call train() to finetune your model.

When loading the model from the same checkpoint that you used for the preprocessing, remember to pass the label2id and id2label maps that you created earlier from the dataset's metadata. Additionally, we specify ignore_mismatched_sizes=True to replace the existing classification head with a new one.

Python
model = AutoModelForObjectDetection.from_pretrained(\n    CHECKPOINT,\n    id2label=id2label,\n    label2id=label2id,\n    anchor_image_size=None,\n    ignore_mismatched_sizes=True,\n)\n
Some weights of RTDetrForObjectDetection were not initialized from the model checkpoint at PekingU/rtdetr_r50vd_coco_o365 and are newly initialized because the shapes did not match:\n- model.decoder.class_embed.0.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.0.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.1.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.1.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.2.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.2.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.3.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.3.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.4.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.4.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.decoder.class_embed.5.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.decoder.class_embed.5.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\n- model.denoising_class_embed.weight: found shape torch.Size([81, 256]) in the checkpoint and torch.Size([54, 256]) in the model instantiated\n- model.enc_score_head.bias: found shape torch.Size([80]) in the checkpoint and torch.Size([53]) in the model instantiated\n- model.enc_score_head.weight: found shape torch.Size([80, 256]) in the checkpoint and torch.Size([53, 256]) in the model instantiated\nYou should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n

In the TrainingArguments use output_dir to specify where to save your model, then configure hyperparameters as you see fit. For num_train_epochs=10 training will take about 15 minutes in Google Colab T4 GPU, increase the number of epoch to get better results.

Important notes:

  • Do not remove unused columns because this will drop the image column. Without the image column, you can't create pixel_values. For this reason, set remove_unused_columns to False.
  • Set eval_do_concat_batches=False to get proper evaluation results. Images have different number of target boxes, if batches are concatenated we will not be able to determine which boxes belongs to particular image.
Python
training_args = TrainingArguments(\n    output_dir=f\"{dataset.name.replace(' ', '-')}-finetune\",\n    num_train_epochs=20,\n    max_grad_norm=0.1,\n    learning_rate=5e-5,\n    warmup_steps=300,\n    per_device_train_batch_size=16,\n    dataloader_num_workers=2,\n    metric_for_best_model=\"eval_map\",\n    greater_is_better=True,\n    load_best_model_at_end=True,\n    eval_strategy=\"epoch\",\n    save_strategy=\"epoch\",\n    save_total_limit=2,\n    remove_unused_columns=False,\n    eval_do_concat_batches=False,\n)\n

Finally, bring everything together, and call train():

Python
trainer = Trainer(\n    model=model,\n    args=training_args,\n    train_dataset=pytorch_dataset_train,\n    eval_dataset=pytorch_dataset_valid,\n    tokenizer=processor,\n    data_collator=collate_fn,\n    compute_metrics=eval_compute_metrics_fn,\n)\n\ntrainer.train()\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#evaluate","title":"Evaluate","text":"Python
# @title Collect predictions\n\ntargets = []\npredictions = []\n\nfor i in range(len(ds_test)):\n    path, sourece_image, annotations = ds_test[i]\n\n    image = Image.open(path)\n    inputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\n    with torch.no_grad():\n        outputs = model(**inputs)\n\n    w, h = image.size\n    results = processor.post_process_object_detection(\n        outputs, target_sizes=[(h, w)], threshold=0.3)\n\n    detections = sv.Detections.from_transformers(results[0])\n\n    targets.append(annotations)\n    predictions.append(detections)\n
Python
# @title Calculate mAP\nmean_average_precision = sv.MeanAveragePrecision.from_detections(\n    predictions=predictions,\n    targets=targets,\n)\n\nprint(f\"map50_95: {mean_average_precision.map50_95:.2f}\")\nprint(f\"map50: {mean_average_precision.map50:.2f}\")\nprint(f\"map75: {mean_average_precision.map75:.2f}\")\n
map50_95: 0.89\nmap50: 0.94\nmap75: 0.94\n
Python
# @title Calculate Confusion Matrix\nconfusion_matrix = sv.ConfusionMatrix.from_detections(\n    predictions=predictions,\n    targets=targets,\n    classes=ds_test.classes\n)\n\n_ = confusion_matrix.plot()\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#save-fine-tuned-model-on-hard-drive","title":"Save fine-tuned model on hard drive","text":"Python
model.save_pretrained(\"/content/rt-detr/\")\nprocessor.save_pretrained(\"/content/rt-detr/\")\n
['/content/rt-detr/preprocessor_config.json']\n
"},{"location":"integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/#inference-with-fine-tuned-rt-detr-model","title":"Inference with fine-tuned RT-DETR model","text":"Python
IMAGE_COUNT = 5\n\nfor i in range(IMAGE_COUNT):\n    path, sourece_image, annotations = ds_test[i]\n\n    image = Image.open(path)\n    inputs = processor(image, return_tensors=\"pt\").to(DEVICE)\n\n    with torch.no_grad():\n        outputs = model(**inputs)\n\n    w, h = image.size\n    results = processor.post_process_object_detection(\n        outputs, target_sizes=[(h, w)], threshold=0.3)\n\n    detections = sv.Detections.from_transformers(results[0]).with_nms(threshold=0.1)\n\n    annotated_images = [\n        annotate(sourece_image, annotations, ds_train.classes),\n        annotate(sourece_image, detections, ds_train.classes)\n    ]\n    grid = sv.create_tiles(\n        annotated_images,\n        titles=['ground truth', 'prediction'],\n        titles_scale=0.5,\n        single_tile_size=(400, 400),\n        tile_padding_color=sv.Color.WHITE,\n        tile_margin_color=sv.Color.WHITE\n    )\n    sv.plot_image(grid, size=(6, 6))\n
"},{"location":"introduction/image_augmentation/","title":"What is image augmentation and how it can improve the performance of deep neural networks","text":"

Deep neural networks require a lot of training data to obtain good results and prevent overfitting. However, it is often very difficult to get enough training samples. Multiple reasons could make it very hard or even impossible to gather enough data:

  • To make a training dataset, you need to obtain images and then label them. For example, you need to assign correct class labels if you have an image classification task. For an object detection task, you need to draw bounding boxes around objects. For a semantic segmentation task, you need to assign a correct class to each input image pixel. This process requires manual labor, and sometimes it could be very costly to label the training data. For example, to correctly label medical images, you need expensive domain experts.

  • Sometimes even collecting training images could be hard. There are many legal restrictions for working with healthcare data, and obtaining it requires a lot of effort. Sometimes getting the training images is more feasible, but it will cost a lot of money. For example, to get satellite images, you need to pay a satellite operator to take those photos. To get images for road scene recognition, you need an operator that will drive a car and collect the required data.

"},{"location":"introduction/image_augmentation/#image-augmentation-to-the-rescue","title":"Image augmentation to the rescue","text":"

Image augmentation is a process of creating new training examples from the existing ones. To make a new sample, you slightly change the original image. For instance, you could make a new image a little brighter; you could cut a piece from the original image; you could make a new image by mirroring the original one, etc.

Here are some examples of transformations of the original image that will create a new training sample.

By applying those transformations to the original training dataset, you could create an almost infinite amount of new training samples.

"},{"location":"introduction/image_augmentation/#how-much-does-image-augmentation-improves-the-quality-and-performance-of-deep-neural-networks","title":"How much does image augmentation improves the quality and performance of deep neural networks","text":"

Basic augmentations techniques were used almost in all papers that describe the state-of-the-art models for image recognition.

AlexNet was the first model that demonstrated exceptional capabilities of using deep neural networks for image recognition. For training, the authors used a set of basic image augmentation techniques. They resized original images to the fixed size of 256 by 256 pixels, and then they cropped patches of size 224 by 224 pixels as well as their horizontal reflections from those resized images. Also, they altered the intensities of the RGB channels in images.

Successive state-of-the-art models such as Inception, ResNet, and EfficientNet also used image augmentation techniques for training.

In 2018 Google published a paper about AutoAugment - an algorithm that automatically discovers the best set of augmentations for the dataset. They showed that a custom set of augmentations improves the performance of the model.

Here is a comparison between a model that used only the base set of augmentations and a model that used a specific set of augmentations discovered by AutoAugment. The table shows Top-1 accuracy (%) on the ImageNet validation set; higher is better.

Model Base augmentations AutoAugment augmentations ResNet-50 76.3 77.6 ResNet-200 78.5 80.0 AmoebaNet-B (6,190) 82.2 82.8 AmoebaNet-C (6,228) 83.1 83.5

The table demonstrates that a diverse set of image augmentations improves the performance of neural networks compared to a base set with only a few most popular transformation techniques.

Augmentations help to fight overfitting and improve the performance of deep neural networks for computer vision tasks such as classification, segmentation, and object detection. The best part is that image augmentations libraries such as Albumentations make it possible to add image augmentations to any computer vision pipeline with minimal effort.

"},{"location":"introduction/why_albumentations/","title":"Why Albumentations","text":""},{"location":"introduction/why_albumentations/#a-single-interface-to-work-with-images-masks-bounding-boxes-and-key-points","title":"A single interface to work with images, masks, bounding boxes, and key points","text":"

Albumentations provides a single interface to work with different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, pose estimation, etc.

"},{"location":"introduction/why_albumentations/#battle-tested","title":"Battle-tested","text":"

The library is widely used in industry, deep learning research, machine learning competitions, and open source projects.

"},{"location":"introduction/why_albumentations/#high-performance","title":"High performance","text":"

Albumentations optimized for maximum speed and performance. Under the hood, the library uses highly optimized functions from OpenCV and NumPy for data processing. We have a regularly updated benchmark that compares the speed of popular image augmentations libraries for the most common image transformations. Albumentations demonstrates the best performance in most cases.

"},{"location":"introduction/why_albumentations/#diverse-set-of-supported-augmentations","title":"Diverse set of supported augmentations","text":"

Albumentations supports more than 60 different image augmentations.

"},{"location":"introduction/why_albumentations/#extensibility","title":"Extensibility","text":"

Albumentations allows to easily add new augmentations and use them in computer vision pipelines through a single interface along with built-in transformations.

"},{"location":"introduction/why_albumentations/#rigorous-testing","title":"Rigorous testing","text":"

Bugs in the augmentation pipeline could silently corrupt the input data. They can easily go unnoticed, but the performance of the models trained with incorrect data will degrade. Albumentations has an extensive test suite that helps to discover bugs during development.

"},{"location":"introduction/why_albumentations/#it-is-open-source-and-mit-licensed","title":"It is open source and MIT licensed","text":"

You can find the source code on GitHub.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/","title":"Why you need a dedicated library for image augmentation","text":"

At first glance, image augmentations look very simple; you apply basic transformations to an image: mirroring, cropping, changing brightness and contrast, etc.

There are a lot of libraries that could do such image transformations. Here is an example of how you could use Pillow, a popular image processing library for Python, to make simple augmentations.

Python
from PIL import Image, ImageEnhance\n\nimage = Image.open(\"parrot.jpg\")\n\nmirrored_image = image.transpose(Image.FLIP_LEFT_RIGHT)\n\nrotated_image = image.rotate(45)\n\nbrightness_enhancer = ImageEnhance.Brightness(image)\nbrighter_image = brightness_enhancer.enhance(factor=1.5)\n

However, this approach has many limitations, and it doesn't handle all cases with image augmentation. An image augmentation library such as Albumentations gives you a lot of advantages.

Here is a list of few pitfalls that augmentation libraries can handle very well.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#the-need-to-apply-the-same-transform-to-an-image-and-for-labels-for-segmentation-object-detection-and-keypoint-detection-tasks","title":"The need to apply the same transform to an image and for labels for segmentation, object detection, and keypoint detection tasks.","text":"

For image classification, you need to modify only an input image and keep output labels intact because output labels are invariant to image modifications.

Note

There are some exceptions to this rule. For example, an image could contain a cat and have an assigned label cat. During image augmentation, if you crop a part of an image that doesn't have a cat on it, then the output label cat becomes wrong and misleading. Usually, you deal with those situations by deciding which augmentations you could apply to a dataset without risking to have problems with incorrect labels.

For segmentation, you need to apply some transformations both to an input image and an output mask. You also have to use the same parameters both for the image transformation and the mask transformation.

Let's look at an example of a semantic segmentation task from Inria Aerial Image Labeling Dataset. The dataset contains aerial photos as well as masks for those photos. Each pixel of the mask is marked either as 1 if the pixel belongs to the class building and 0 otherwise.

There are two types of image augmentations: pixel-level augmentations and spatial-level augmentations.

Pixel-level augmentations change the values of pixels of the original image, but they don't change the output mask. Image transformations such as changing brightness or contrast of adjusting values of the RGB-palette of the image are pixel-level augmentations.

We modify the input image by adjusting its brightness, but we keep the output mask intact.

On the contrary, spatial-level augmentations change both the image and the mask. When you apply image transformations such as mirroring or rotation or cropping a part of the input image, you also need to apply the same transformation to the output label to preserve its correctness.

We rotate both the input image and the output mask. We use the same set of transformations with the same parameters, both for the image and the mask.

The same is true for object detection tasks. For pixel-level augmentations, you only need to change the input image. With spatial-level augmentations, you need to apply the same transformation not only to the image but for bounding boxes coordinates as well. After applying spatial-level augmentations, you need to update coordinates of bounding boxes to represent the correct locations of objects on the augmented image.

Pixel-level augmentations such as brightness adjustment change only the input image but not the coordinates of bounding boxes. Spatial-level augmentations such as mirroring and cropping a part of the image change both the input image and the bounding boxes' coordinates.

Albumentations knows how to correctly apply transformation both to the input data as well as the output labels.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#working-with-probabilities","title":"Working with probabilities","text":"

During training, you usually want to apply augmentations with a probability of less than 100% since you also need to have the original images in your training pipeline. Also, it is beneficial to be able to control the magnitude of image augmentation, how much does the augmentation change the original image. If the original dataset is large, you could apply only the basic augmentations with probability around 10-30% and with a small magnitude of changes. If the dataset is small, you need to act more aggressively with augmentations to prevent overfitting of neural networks, so you usually need to increase the probability of applying each augmentation to 40-50% and increase the magnitude of changes the augmentation makes to the image.

Image augmentation libraries allow you to set the required probabilities and the magnitude of values for each transformation.

"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#declarative-definition-of-the-augmentation-pipeline-and-unified-interface","title":"Declarative definition of the augmentation pipeline and unified interface","text":"

Usually, you want to apply not a single augmentation, but a set of augmentations with specific parameters such as probability and magnitude of changes. Augmentation libraries allow you to declare such a pipeline in a single place and then use it for image transformation through a unified interface. Some libraries can store and load transformation parameters to formats such as JSON, YAML, etc.

Here is an example definition of an augmentation pipeline. This pipeline will first crop a random 512px x 512px part of the input image. Then with probability 30%, it will randomly change brightness and contrast of that crop. Finally, with probability 50%, it will horizontally flip the resulting image.

Python
import albumentations as A\n\ntransform = A.Compose([\n    A.RandomCrop(512, 512),\n    A.RandomBrightnessContrast(p=0.3),\n    A.HorizontalFlip(p=0.5),\n])\n
"},{"location":"introduction/why_you_need_a_dedicated_library_for_image_augmentation/#rigorous-testing","title":"Rigorous testing","text":"

A bug in the augmentation pipeline could easily go unnoticed. A buggy pipeline could silently corrupt input data. There won't be any exceptions and code failures, but the performance of trained neural networks will degrade because they received a garbage input during training. Augmentation libraries usually have large test suites that capture regressions during development. Also large user base helps to find unnoticed bugs and report them to developers.

"}]} \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml index eebee092..517e522f 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -2,366 +2,366 @@ https://albumentations.ai/docs/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/CONTRIBUTING/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/benchmarking_results/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/faq/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/frameworks_and_libraries/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/full_reference/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/autoalbument/benchmarks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/channel_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/coarse_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/grid_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/mask_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/xy_masking/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/rotate/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/bbox_utils/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/composition/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/keypoints_utils/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/serialization/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/transforms_interface/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/pytorch/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/pytorch/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/benchmarks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/custom_model/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/docker/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/faq/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/how_to_use/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/installation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/introduction/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/metrics/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/search_algorithms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/tuning_parameters/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/cifar10/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/cityscapes/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/imagenet/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/list/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/pascal_voc/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/svhn/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/contributing/coding_guidelines/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/contributing/environment_setup/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/examples/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/blog_posts_podcasts_talks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/books/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/online_courses/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/augmentation_mapping/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/image_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/installation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/keypoints_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/mask_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/setting_probabilities/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/simultaneous_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/transforms_and_targets/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/video_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/volumetric_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/fiftyone/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/image_classification_albumentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/object_detection/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/image_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/why_albumentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/why_you_need_a_dedicated_library_for_image_augmentation/ - 2024-12-23 + 2024-12-24 \ No newline at end of file diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz index d6aa4c7c246f7a44fa55b89acfe81634f44ba5ca..17ef6d8a13f223f2e400c076c1d83b89546aff33 100644 GIT binary patch delta 290 zcmV+-0p0$`2ge5oABzYGfca^W2Oj}*ktd{oZT0Y2J-+2G=eAi${VL@@$out=4=;bd z|K;P$`wze0TzJDuA4c$7*1Pf;l8v|5KnH)_UZ#f;_vE)hfoqjDjR$Y=_CFpb!;0_F zvn8;w^e$N|1_n}p=_87zr4fu%(84q~iK6S0)Ah85qT zXG>sV>0Po`3=E|H(nl0aOCuPkpoM8}5=GY~r}G_HQCL0Fh|wD@iYj_=p(8)a@-Rq$ zk-mKG5~F1?Ha2;AKtjF$#3t0)w5LfTkx*04a6)YAlbumentations: fast and flexible image augmentations

Do more with less data

Albumentations is a computer vision tool that boosts the performance of deep convolutional neural networks.

The library is widely used in industry, deep learning research, machine learning competitions, and open source projects.

Albumentations example

Community-Driven Project, Supported By

Albumentations thrives on developer contributions. We appreciate our sponsors who help sustain the project's infrastructure.

Become a Sponsor
View sponsorship tiers and benefits on GitHub Sponsors

Why Albumentations

The fastest and most flexible image augmentation library, trusted by thousands of AI engineers and researchers worldwide

Lightning Fast

Up to 10x faster than other libraries. See benchmarks

Versatile

Supports classification, segmentation, detection, and more tasks out of the box

Easy to Use

Simple, intuitive API with comprehensive documentation and examples

Albumentations is a Python library for image augmentations that provides:

  • Optimized performance for production environments
  • Rich variety of transform operations
  • Support for all major computer vision tasks
  • Seamless integration with PyTorch, TensorFlow, and other frameworks

Community Feedback

Community feedback
CEO of Datature
Community feedback
Kaggle Competitions Grandmaster. Top 1 in the world.
Community feedback
Computer Vision Engineer

Different tasks

Different tasks

Albumentations supports different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, and pose estimation.

Computer vision tasks

Different domains

Different domains

Albumentations works well with data from different domains: photos, medical images, satellite imagery, manufacturing and industrial applications, Generative Adversarial Networks.

Different domains

Seamless integration with deep learning frameworks

Seamless integration with deep learning frameworks

Albumentations can work with various deep learning frameworks such as PyTorch and Keras. The library is a part of the PyTorch ecosystem. MMDetection and YOLOv5 use Albumentations.

Deep learning frameworks

Getting started

Albumentations requires Python 3.9 or higher. To install the library from PyPI run

pip install -U albumentations
Open documentation

Support Open Source Development

Albumentations is a free, open-source project maintained by a dedicated team of developers

Your sponsorship helps us maintain high-quality code, provide timely updates, and develop new features

Individual Sponsors

Support open source with a monthly contribution of any size. Every dollar helps maintain and improve Albumentations.

Sponsor

Company Sponsorship

Companies using Albumentations can become official sponsors, getting their logo featured on our website and documentation.

View Sponsorship Tiers
100% of sponsorships go directly to supporting development and maintenance.

Citing

If you find this library useful for your research, please consider citing Albumentations: Fast and Flexible Image Augmentations:

@Article{info11020125,
+Albumentations: fast and flexible image augmentations

Do more with less data

Albumentations is a computer vision tool that boosts the performance of deep convolutional neural networks.

The library is widely used in industry, deep learning research, machine learning competitions, and open source projects.

Albumentations example

Community-Driven Project, Supported By

Albumentations thrives on developer contributions. We appreciate our sponsors who help sustain the project's infrastructure.

Become a Sponsor
View sponsorship tiers and benefits on GitHub Sponsors

Why Albumentations

The fastest and most flexible image augmentation library, trusted by thousands of AI engineers and researchers worldwide

Lightning Fast

Up to 10x faster than other libraries. See benchmarks

Versatile

Supports classification, segmentation, detection, and more tasks out of the box

Easy to Use

Simple, intuitive API with comprehensive documentation and examples

Albumentations is a Python library for image augmentations that provides:

  • Optimized performance for production environments
  • Rich variety of transform operations
  • Support for all major computer vision tasks
  • Seamless integration with PyTorch, TensorFlow, and other frameworks

Community Feedback

Community feedback
CEO of Datature
Community feedback
Kaggle Competitions Grandmaster. Top 1 in the world.
Community feedback
Computer Vision Engineer

Different tasks

Different tasks

Albumentations supports different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, and pose estimation.

Computer vision tasks

Different domains

Different domains

Albumentations works well with data from different domains: photos, medical images, satellite imagery, manufacturing and industrial applications, Generative Adversarial Networks.

Different domains

Seamless integration with deep learning frameworks

Seamless integration with deep learning frameworks

Albumentations can work with various deep learning frameworks such as PyTorch and Keras. The library is a part of the PyTorch ecosystem. MMDetection and YOLOv5 use Albumentations.

Deep learning frameworks

Getting started

Albumentations requires Python 3.9 or higher. To install the library from PyPI run

pip install -U albumentations
Open documentation

Support Open Source Development

Albumentations is a free, open-source project maintained by a dedicated team of developers

Your sponsorship helps us maintain high-quality code, provide timely updates, and develop new features

Individual Sponsors

Support open source with a monthly contribution of any size. Every dollar helps maintain and improve Albumentations.

Sponsor

Company Sponsorship

Companies using Albumentations can become official sponsors, getting their logo featured on our website and documentation.

View Sponsorship Tiers
100% of sponsorships go directly to supporting development and maintenance.

Citing

If you find this library useful for your research, please consider citing Albumentations: Fast and Flexible Image Augmentations:

@Article{info11020125,
     AUTHOR = {Buslaev, Alexander and Iglovikov, Vladimir I. and Khvedchenya, Eugene and Parinov, Alex and Druzhinin, Mikhail and Kalinin, Alexandr A.},
     TITLE = {Albumentations: Fast and Flexible Image Augmentations},
     JOURNAL = {Information},
@@ -9,4 +9,4 @@
     URL = {https://www.mdpi.com/2078-2489/11/2/125},
     ISSN = {2078-2489},
     DOI = {10.3390/info11020125}
-}
\ No newline at end of file +}
\ No newline at end of file diff --git a/index.txt b/index.txt index c9f480f2..17e31a41 100755 --- a/index.txt +++ b/index.txt @@ -12,7 +12,7 @@ e:I[6213,[],"MetadataBoundary"] 2:HL["/_next/static/media/463dafcda517f24f-s.p.woff","font",{"crossOrigin":"","type":"font/woff"}] 3:HL["/_next/static/css/8043a7c984777fb1.css","style"] 4:HL["/_next/static/css/4d3d9169b46fed63.css","style"] -0:{"P":null,"b":"ZNcQrWMYk7ymGN9y8PjnQ","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$5","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L6",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L7",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L8",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L9","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L9","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L9","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L9","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$La",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["__PAGE__",["$","$5","c",{"children":["$Lb",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/4d3d9169b46fed63.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$Lc",null,{"children":"$Ld"}]]}],{},null]},null],["$","$5","h",{"children":[null,["$","$5","MBt7JfAd9ag4e5v5N-19O",{"children":[["$","$Le",null,{"children":"$Lf"}],["$","$L10",null,{"children":"$L11"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$12","$undefined"],"s":false,"S":true} +0:{"P":null,"b":"vJuFcpWvbN6zbAub4gcIZ","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$5","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L6",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L7",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L8",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L9","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L9","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L9","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L9","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$La",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["__PAGE__",["$","$5","c",{"children":["$Lb",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/4d3d9169b46fed63.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$Lc",null,{"children":"$Ld"}]]}],{},null]},null],["$","$5","h",{"children":[null,["$","$5","Xb-XWebX4_Ox_oTcMoDXa",{"children":[["$","$Le",null,{"children":"$Lf"}],["$","$L10",null,{"children":"$L11"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$12","$undefined"],"s":false,"S":true} 11:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] f:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"Albumentations: fast and flexible image augmentations"}],["$","meta","2",{"name":"description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","3",{"name":"robots","content":"index, follow"}],["$","link","4",{"rel":"canonical","href":"https://albumentations.ai/"}],["$","meta","5",{"property":"og:title","content":"Albumentations: fast and flexible image augmentations"}],["$","meta","6",{"property":"og:description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","7",{"property":"og:url","content":"https://albumentations.ai/"}],["$","meta","8",{"property":"og:site_name","content":"Albumentations"}],["$","meta","9",{"property":"og:locale","content":"en_US"}],["$","meta","10",{"property":"og:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","meta","11",{"property":"og:image:width","content":"1200"}],["$","meta","12",{"property":"og:image:height","content":"630"}],["$","meta","13",{"property":"og:image:alt","content":"Albumentations"}],["$","meta","14",{"property":"og:type","content":"website"}],["$","meta","15",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","16",{"name":"twitter:site","content":"@albumentations"}],["$","meta","17",{"name":"twitter:creator","content":"@viglovikov"}],["$","meta","18",{"name":"twitter:title","content":"Albumentations: fast and flexible image augmentations"}],["$","meta","19",{"name":"twitter:description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","20",{"name":"twitter:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","link","21",{"rel":"icon","href":"/icon.svg?ed95530d83f93aed","type":"image/svg+xml","sizes":"any"}]] d:null @@ -22,4 +22,4 @@ d:null 16:I[9264,["565","static/chunks/565-7d6f0e76202f7b1c.js","396","static/chunks/396-4e8a41fcd07efacf.js","158","static/chunks/158-fba4c5b26e754598.js","974","static/chunks/app/page-01034d6f519d879c.js"],"default"] 17:I[4755,["565","static/chunks/565-7d6f0e76202f7b1c.js","396","static/chunks/396-4e8a41fcd07efacf.js","158","static/chunks/158-fba4c5b26e754598.js","974","static/chunks/app/page-01034d6f519d879c.js"],"default"] 18:I[7970,["565","static/chunks/565-7d6f0e76202f7b1c.js","396","static/chunks/396-4e8a41fcd07efacf.js","158","static/chunks/158-fba4c5b26e754598.js","974","static/chunks/app/page-01034d6f519d879c.js"],"Image"] -b:["$","main",null,{"children":[["$","$L13",null,{"starsCount":14419,"downloadsCount":5135449}],["$","$L14",null,{}],["$","$L15",null,{}],["$","$L16",null,{}],["$","$L17",null,{}],["$","div",null,{"className":"features","children":[["$","div","Different tasks",{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Different tasks"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center ","children":[["$","div",null,{"className":"md:pr-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Different tasks"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations supports different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, and pose estimation."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/tasks.png","alt":"Computer vision tasks","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}],["$","div","Different domains",{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Different domains"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center md:flex-row-reverse","children":[["$","div",null,{"className":"md:pl-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Different domains"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations works well with data from different domains: photos, medical images, satellite imagery, manufacturing and industrial applications, Generative Adversarial Networks."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/domains.png","alt":"Different domains","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}],["$","div","Seamless integration with deep learning frameworks",{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Seamless integration with deep learning frameworks"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center ","children":[["$","div",null,{"className":"md:pr-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Seamless integration with deep learning frameworks"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations can work with various deep learning frameworks such as PyTorch and Keras. The library is a part of the PyTorch ecosystem. MMDetection and YOLOv5 use Albumentations."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/deep_learning_frameworks.png","alt":"Deep learning frameworks","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}]]}],["$","div",null,{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Getting started"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"max-w-3xl mx-auto text-center","children":[["$","p",null,{"className":"text-lg mb-4","children":"Albumentations requires Python 3.9 or higher. To install the library from PyPI run"}],["$","div",null,{"className":"bg-white border border-gray-400 rounded-lg p-4 mb-6 font-mono text-xl","children":"pip install -U albumentations"}],["$","$L9",null,{"href":"/docs","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md bg-blue-600 text-white hover:bg-blue-700 px-6 py-3 text-lg","children":[["$","i",null,{"className":"fas fa-angle-right mr-2"}],"Open documentation"]}]]}]}]]}]}],["$","div",null,{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Support Open Source Development"}],["$","div",null,{"className":"max-w-5xl mx-auto","children":["$","div",null,{"className":"space-y-8","children":[["$","div",null,{"className":"text-center max-w-3xl mx-auto","children":[["$","p",null,{"className":"text-xl text-gray-700 mb-4","children":"Albumentations is a free, open-source project maintained by a dedicated team of developers"}],["$","p",null,{"className":"text-gray-600","children":"Your sponsorship helps us maintain high-quality code, provide timely updates, and develop new features"}]]}],["$","div",null,{"className":"grid md:grid-cols-2 gap-8","children":[["$","div",null,{"className":"bg-white p-6 rounded-xl shadow-sm border border-gray-100","children":[["$","h3",null,{"className":"text-lg font-medium mb-3 flex items-center gap-2","children":[["$","i",null,{"className":"fas fa-heart text-pink-500"}],"Individual Sponsors"]}],["$","p",null,{"className":"text-gray-600 mb-4","children":"Support open source with a monthly contribution of any size. Every dollar helps maintain and improve Albumentations."}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":[["$","i",null,{"className":"fa fa-heart mr-2"}],"Sponsor"]}]]}],["$","div",null,{"className":"bg-white p-6 rounded-xl shadow-sm border border-gray-100","children":[["$","h3",null,{"className":"text-lg font-medium mb-3 flex items-center gap-2","children":[["$","i",null,{"className":"fas fa-building text-blue-500"}],"Company Sponsorship"]}],["$","p",null,{"className":"text-gray-600 mb-4","children":"Companies using Albumentations can become official sponsors, getting their logo featured on our website and documentation."}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","target":"_blank","rel":"noopener noreferrer","className":"inline-flex items-center gap-2 px-4 py-2 bg-blue-50 hover:bg-blue-100 text-blue-700 rounded-lg transition-colors","children":[["$","i",null,{"className":"fab fa-github"}],"View Sponsorship Tiers"]}]]}]]}],["$","div",null,{"className":"text-center text-sm text-gray-500","children":"100% of sponsorships go directly to supporting development and maintenance."}]]}]}]]}]}],["$","div",null,{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Citing"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"max-w-4xl mx-auto","children":[["$","p",null,{"className":"text-lg mb-4","children":["If you find this library useful for your research, please consider citing"," ",["$","a",null,{"href":"https://www.mdpi.com/2078-2489/11/2/125","target":"_blank","rel":"noopener noreferrer","className":"text-blue-600 hover:text-blue-800 underline","children":"Albumentations: Fast and Flexible Image Augmentations"}],":"]}],["$","pre",null,{"className":"bg-gray-50 border border-gray-300 rounded-lg p-4 overflow-x-auto text-sm leading-relaxed","children":["$","code",null,{"children":"@Article{info11020125,\n AUTHOR = {Buslaev, Alexander and Iglovikov, Vladimir I. and Khvedchenya, Eugene and Parinov, Alex and Druzhinin, Mikhail and Kalinin, Alexandr A.},\n TITLE = {Albumentations: Fast and Flexible Image Augmentations},\n JOURNAL = {Information},\n VOLUME = {11},\n YEAR = {2020},\n NUMBER = {2},\n ARTICLE-NUMBER = {125},\n URL = {https://www.mdpi.com/2078-2489/11/2/125},\n ISSN = {2078-2489},\n DOI = {10.3390/info11020125}\n}"}]}]]}]}]]}]}]]}] +b:["$","main",null,{"children":[["$","$L13",null,{"starsCount":14422,"downloadsCount":5151795}],["$","$L14",null,{}],["$","$L15",null,{}],["$","$L16",null,{}],["$","$L17",null,{}],["$","div",null,{"className":"features","children":[["$","div","Different tasks",{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Different tasks"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center ","children":[["$","div",null,{"className":"md:pr-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Different tasks"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations supports different computer vision tasks such as classification, semantic segmentation, instance segmentation, object detection, and pose estimation."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/tasks.png","alt":"Computer vision tasks","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}],["$","div","Different domains",{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Different domains"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center md:flex-row-reverse","children":[["$","div",null,{"className":"md:pl-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Different domains"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations works well with data from different domains: photos, medical images, satellite imagery, manufacturing and industrial applications, Generative Adversarial Networks."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/domains.png","alt":"Different domains","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}],["$","div","Seamless integration with deep learning frameworks",{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Seamless integration with deep learning frameworks"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"grid grid-cols-1 md:grid-cols-2 gap-8 items-center ","children":[["$","div",null,{"className":"md:pr-8","children":[["$","h2",null,{"className":"text-2xl font-semibold mb-4","children":"Seamless integration with deep learning frameworks"}],["$","p",null,{"className":"text-gray-600 text-lg","children":"Albumentations can work with various deep learning frameworks such as PyTorch and Keras. The library is a part of the PyTorch ecosystem. MMDetection and YOLOv5 use Albumentations."}]]}],["$","div",null,{"children":["$","$L18",null,{"src":"/assets/custom/deep_learning_frameworks.png","alt":"Deep learning frameworks","width":500,"height":300,"className":"w-full h-auto"}]}]]}]}]]}]}]]}],["$","div",null,{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Getting started"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"max-w-3xl mx-auto text-center","children":[["$","p",null,{"className":"text-lg mb-4","children":"Albumentations requires Python 3.9 or higher. To install the library from PyPI run"}],["$","div",null,{"className":"bg-white border border-gray-400 rounded-lg p-4 mb-6 font-mono text-xl","children":"pip install -U albumentations"}],["$","$L9",null,{"href":"/docs","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md bg-blue-600 text-white hover:bg-blue-700 px-6 py-3 text-lg","children":[["$","i",null,{"className":"fas fa-angle-right mr-2"}],"Open documentation"]}]]}]}]]}]}],["$","div",null,{"className":"bg-gray-50 py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Support Open Source Development"}],["$","div",null,{"className":"max-w-5xl mx-auto","children":["$","div",null,{"className":"space-y-8","children":[["$","div",null,{"className":"text-center max-w-3xl mx-auto","children":[["$","p",null,{"className":"text-xl text-gray-700 mb-4","children":"Albumentations is a free, open-source project maintained by a dedicated team of developers"}],["$","p",null,{"className":"text-gray-600","children":"Your sponsorship helps us maintain high-quality code, provide timely updates, and develop new features"}]]}],["$","div",null,{"className":"grid md:grid-cols-2 gap-8","children":[["$","div",null,{"className":"bg-white p-6 rounded-xl shadow-sm border border-gray-100","children":[["$","h3",null,{"className":"text-lg font-medium mb-3 flex items-center gap-2","children":[["$","i",null,{"className":"fas fa-heart text-pink-500"}],"Individual Sponsors"]}],["$","p",null,{"className":"text-gray-600 mb-4","children":"Support open source with a monthly contribution of any size. Every dollar helps maintain and improve Albumentations."}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":[["$","i",null,{"className":"fa fa-heart mr-2"}],"Sponsor"]}]]}],["$","div",null,{"className":"bg-white p-6 rounded-xl shadow-sm border border-gray-100","children":[["$","h3",null,{"className":"text-lg font-medium mb-3 flex items-center gap-2","children":[["$","i",null,{"className":"fas fa-building text-blue-500"}],"Company Sponsorship"]}],["$","p",null,{"className":"text-gray-600 mb-4","children":"Companies using Albumentations can become official sponsors, getting their logo featured on our website and documentation."}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","target":"_blank","rel":"noopener noreferrer","className":"inline-flex items-center gap-2 px-4 py-2 bg-blue-50 hover:bg-blue-100 text-blue-700 rounded-lg transition-colors","children":[["$","i",null,{"className":"fab fa-github"}],"View Sponsorship Tiers"]}]]}]]}],["$","div",null,{"className":"text-center text-sm text-gray-500","children":"100% of sponsorships go directly to supporting development and maintenance."}]]}]}]]}]}],["$","div",null,{"className":"bg-white py-12 md:py-16","children":["$","div",null,{"className":"container mx-auto px-4","children":[["$","h2",null,{"className":"text-2xl md:text-3xl font-medium text-center mb-8","children":"Citing"}],["$","div",null,{"className":"","children":["$","div",null,{"className":"max-w-4xl mx-auto","children":[["$","p",null,{"className":"text-lg mb-4","children":["If you find this library useful for your research, please consider citing"," ",["$","a",null,{"href":"https://www.mdpi.com/2078-2489/11/2/125","target":"_blank","rel":"noopener noreferrer","className":"text-blue-600 hover:text-blue-800 underline","children":"Albumentations: Fast and Flexible Image Augmentations"}],":"]}],["$","pre",null,{"className":"bg-gray-50 border border-gray-300 rounded-lg p-4 overflow-x-auto text-sm leading-relaxed","children":["$","code",null,{"children":"@Article{info11020125,\n AUTHOR = {Buslaev, Alexander and Iglovikov, Vladimir I. and Khvedchenya, Eugene and Parinov, Alex and Druzhinin, Mikhail and Kalinin, Alexandr A.},\n TITLE = {Albumentations: Fast and Flexible Image Augmentations},\n JOURNAL = {Information},\n VOLUME = {11},\n YEAR = {2020},\n NUMBER = {2},\n ARTICLE-NUMBER = {125},\n URL = {https://www.mdpi.com/2078-2489/11/2/125},\n ISSN = {2078-2489},\n DOI = {10.3390/info11020125}\n}"}]}]]}]}]]}]}]]}] diff --git a/people/index.html b/people/index.html index 9cfcc148..8f769e7d 100755 --- a/people/index.html +++ b/people/index.html @@ -1 +1 @@ -Albumentations: fast and flexible image augmentations

Core team

Vladimir Iglovikov

Vladimir Iglovikov

Honorary Developers. Were behind the creation of the library. Sadly not active anymore.

Alexander Buslaev

Alexander Buslaev

Alex Parinov

Alex Parinov

Evegene Khvedchenya

Evegene Khvedchenya

Mikhail Druzhinin

Mikhail Druzhinin

Contributors

ternaus
creafz
Dipet
albu
BloodAxe
ayasyrev
vfdev-5
arsenyinfo
qubvel
ZFTurbo
i-aki-y
MichaelMonashev
zakajd
victor1cea
akarsakov
IlyaOvodov
LinaShiryaeva
IliaLarchenko
StrikerRUS
Arquestro
jangop
momincks
guillaume-rochette-oxb
bfialkoff
VirajBagal
5n7-sk
sergei3000
libfun
gavrin-s
SunQpark
alimbekovKZ
onurtore
ocourtin
Aloqeely
ajinkyakhadilkar
alekseynp
alexander-rakhlin
Andredance
toshiks
erikgaas
domef
4pygmalion
JonasKlotz
KiriLev
marcocaccin
mys007
PerchunPak
philipp-fischer
huuquan1994
ruancomelli
kaichoulyc
rsokl
bryant1410
bes-dev
matsumotosan
tashay
tmramalho
timgates42
tabula-rosa
rbu
ORippler
nathanhubens
NatanBagrov
namangup
b0nce
amirassov
deleomike
mennohofste
matejpekar
Matthew-J-Payne
gogetron
mrsmrynk
daisukelab
Diyago
thomaoc1
tatigabru
shyn
ryoryon66
oguz-hanoglu
notmatthancock
loicmagne
kmistry-wx
jovenwayfarer
jasonrock-a3
iRyoka
pomacanthidae
haarisr
gyaneshwar-sunkara
dmitrie-ai
dependabot[bot]
bonlime
aaroswings
zahragolpa
yisaienkov
WesleyYue
notplus
vladserkoff
johngull
Vcv85
t-hanya
dskkato
DBusAI
ChristofHenkel
Juphex
cdicle
Multihuntr
spsancti
Callidior
poke1024
bmabey
artyompal
sneddy
AndreyGurevich
Erlemar
agchang-cgl
alicangok
golunovas
alxndrkalinin
kinoooshnik
Alex-JG3
aidonchuk
alessiobonfiglio
belskikh
aaronzs
AaronPinto
ah651
maremun
InCogNiTo124
maksimovkonstantin
Kupchanski
kirillbobyrev
immortalCO
jveitchmichaelis
Erotemic
xiaoyuan0203
jaehyuck0103
cannon
ifeherva
Ingwar
hoel-bagard
henrique
hassiahk
steermomo
Gurubaseio
georgymironov
GalDude33
farizrahman4u
maruschin
ErlingLie
zetyquickly
plashchynski
cortwave
Datasciensyash

Community-Driven Project, Supported By

Albumentations thrives on developer contributions. We appreciate our sponsors who help sustain the project's infrastructure.

Become a Sponsor
View sponsorship tiers and benefits on GitHub Sponsors
\ No newline at end of file +Albumentations: fast and flexible image augmentations

Core team

Vladimir Iglovikov

Vladimir Iglovikov

Honorary Developers. Were behind the creation of the library. Sadly not active anymore.

Alexander Buslaev

Alexander Buslaev

Alex Parinov

Alex Parinov

Evegene Khvedchenya

Evegene Khvedchenya

Mikhail Druzhinin

Mikhail Druzhinin

Contributors

ternaus
creafz
Dipet
albu
BloodAxe
ayasyrev
vfdev-5
arsenyinfo
qubvel
ZFTurbo
i-aki-y
MichaelMonashev
zakajd
victor1cea
akarsakov
IlyaOvodov
LinaShiryaeva
IliaLarchenko
StrikerRUS
Arquestro
jangop
momincks
guillaume-rochette-oxb
bfialkoff
VirajBagal
5n7-sk
sergei3000
libfun
gavrin-s
SunQpark
alimbekovKZ
onurtore
ocourtin
Aloqeely
ajinkyakhadilkar
alekseynp
alexander-rakhlin
Andredance
toshiks
erikgaas
domef
4pygmalion
JonasKlotz
KiriLev
marcocaccin
mys007
PerchunPak
philipp-fischer
huuquan1994
ruancomelli
kaichoulyc
rsokl
bryant1410
bes-dev
matsumotosan
tashay
tmramalho
timgates42
tabula-rosa
rbu
ORippler
nathanhubens
NatanBagrov
namangup
b0nce
amirassov
deleomike
mennohofste
matejpekar
Matthew-J-Payne
gogetron
mrsmrynk
daisukelab
Diyago
thomaoc1
tatigabru
shyn
ryoryon66
oguz-hanoglu
notmatthancock
loicmagne
kmistry-wx
jovenwayfarer
jasonrock-a3
iRyoka
pomacanthidae
haarisr
gyaneshwar-sunkara
dmitrie-ai
dependabot[bot]
bonlime
aaroswings
zahragolpa
yisaienkov
WesleyYue
notplus
vladserkoff
johngull
Vcv85
t-hanya
dskkato
DBusAI
ChristofHenkel
Juphex
cdicle
Multihuntr
spsancti
Callidior
poke1024
bmabey
artyompal
sneddy
AndreyGurevich
Erlemar
agchang-cgl
alicangok
golunovas
alxndrkalinin
kinoooshnik
Alex-JG3
aidonchuk
alessiobonfiglio
belskikh
aaronzs
AaronPinto
ah651
maremun
InCogNiTo124
maksimovkonstantin
Kupchanski
kirillbobyrev
immortalCO
jveitchmichaelis
Erotemic
xiaoyuan0203
jaehyuck0103
cannon
ifeherva
Ingwar
hoel-bagard
henrique
hassiahk
steermomo
Gurubaseio
georgymironov
GalDude33
farizrahman4u
maruschin
ErlingLie
zetyquickly
plashchynski
cortwave
Datasciensyash

Community-Driven Project, Supported By

Albumentations thrives on developer contributions. We appreciate our sponsors who help sustain the project's infrastructure.

Become a Sponsor
View sponsorship tiers and benefits on GitHub Sponsors
\ No newline at end of file diff --git a/people/index.txt b/people/index.txt index 47e2fa0e..ab237af1 100755 --- a/people/index.txt +++ b/people/index.txt @@ -11,7 +11,7 @@ f:I[6213,[],"ViewportBoundary"] 1:HL["/_next/static/media/4473ecc91f70f139-s.p.woff","font",{"crossOrigin":"","type":"font/woff"}] 2:HL["/_next/static/media/463dafcda517f24f-s.p.woff","font",{"crossOrigin":"","type":"font/woff"}] 3:HL["/_next/static/css/8043a7c984777fb1.css","style"] -0:{"P":null,"b":"ZNcQrWMYk7ymGN9y8PjnQ","p":"","c":["","people",""],"i":false,"f":[[["",{"children":["people",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$4","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L5",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L8","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L8","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L8","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L8","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$L9",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["people",["$","$4","c",{"children":[null,["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children","people","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]]}],{"children":["__PAGE__",["$","$4","c",{"children":["$La",null,["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null]},null],["$","$4","h",{"children":[null,["$","$4","mvY_dJGdwDys125CocXmB",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$11","$undefined"],"s":false,"S":true} +0:{"P":null,"b":"vJuFcpWvbN6zbAub4gcIZ","p":"","c":["","people",""],"i":false,"f":[[["",{"children":["people",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$4","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L5",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L8","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L8","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L8","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L8","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$L9",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["people",["$","$4","c",{"children":[null,["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children","people","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]]}],{"children":["__PAGE__",["$","$4","c",{"children":["$La",null,["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null]},null],["$","$4","h",{"children":[null,["$","$4","Ms9w_hju4wmo-CT1B5ygd",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$11","$undefined"],"s":false,"S":true} 10:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] e:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"Albumentations: fast and flexible image augmentations"}],["$","meta","2",{"name":"description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","3",{"name":"robots","content":"index, follow"}],["$","link","4",{"rel":"canonical","href":"https://albumentations.ai/"}],["$","meta","5",{"property":"og:title","content":"Albumentations: fast and flexible image augmentations"}],["$","meta","6",{"property":"og:description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","7",{"property":"og:url","content":"https://albumentations.ai/"}],["$","meta","8",{"property":"og:site_name","content":"Albumentations"}],["$","meta","9",{"property":"og:locale","content":"en_US"}],["$","meta","10",{"property":"og:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","meta","11",{"property":"og:image:width","content":"1200"}],["$","meta","12",{"property":"og:image:height","content":"630"}],["$","meta","13",{"property":"og:image:alt","content":"Albumentations"}],["$","meta","14",{"property":"og:type","content":"website"}],["$","meta","15",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","16",{"name":"twitter:site","content":"@albumentations"}],["$","meta","17",{"name":"twitter:creator","content":"@viglovikov"}],["$","meta","18",{"name":"twitter:title","content":"Albumentations: fast and flexible image augmentations"}],["$","meta","19",{"name":"twitter:description","content":"Albumentations provides a comprehensive, high-performance framework for augmenting images to improve machine learning models."}],["$","meta","20",{"name":"twitter:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","link","21",{"rel":"icon","href":"/icon.svg?ed95530d83f93aed","type":"image/svg+xml","sizes":"any"}]] c:null diff --git a/sitemap.xml b/sitemap.xml index eb0ba97e..6e5ac086 100755 --- a/sitemap.xml +++ b/sitemap.xml @@ -1,378 +1,378 @@ https://albumentations.ai/docs/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/CONTRIBUTING/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/benchmarking_results/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/faq/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/frameworks_and_libraries/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/full_reference/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/autoalbument/benchmarks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/blur/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/crops/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/domain_adaptation/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/channel_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/coarse_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/grid_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/mask_dropout/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/dropout/xy_masking/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/resize/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/rotate/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/geometric/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/mixing/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/functional/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/augmentations/transforms3d/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/bbox_utils/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/composition/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/keypoints_utils/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/serialization/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/core/transforms_interface/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/pytorch/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/api_reference/pytorch/transforms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/benchmarks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/custom_model/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/docker/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/faq/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/how_to_use/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/installation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/introduction/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/metrics/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/search_algorithms/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/tuning_parameters/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/cifar10/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/cityscapes/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/imagenet/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/list/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/pascal_voc/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/autoalbument/examples/svhn/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/contributing/coding_guidelines/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/contributing/environment_setup/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/examples/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/blog_posts_podcasts_talks/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/books/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/external_resources/online_courses/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/augmentation_mapping/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/image_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/installation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/keypoints_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/mask_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/setting_probabilities/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/simultaneous_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/transforms_and_targets/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/video_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/getting_started/volumetric_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/fiftyone/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/image_classification_albumentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/huggingface/object_detection/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/integrations/roboflow/train-rt-detr-on-custom-dataset-with-transformers/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/image_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/why_albumentations/ - 2024-12-23 + 2024-12-24 https://albumentations.ai/docs/introduction/why_you_need_a_dedicated_library_for_image_augmentation/ - 2024-12-23 + 2024-12-24 https://albumentations.ai - 2024-12-23 + 2024-12-24 daily https://albumentations.ai/people - 2024-12-23 + 2024-12-24 daily https://albumentations.ai/testimonials - 2024-12-23 + 2024-12-24 daily diff --git a/testimonials/index.html b/testimonials/index.html index e479c1c4..09fdff11 100755 --- a/testimonials/index.html +++ b/testimonials/index.html @@ -1 +1 @@ -Community Testimonials | Albumentations

Community Feedback

Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
\ No newline at end of file +Community Testimonials | Albumentations

Community Feedback

Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
Community feedback
\ No newline at end of file diff --git a/testimonials/index.txt b/testimonials/index.txt index 75991013..c7bb124e 100755 --- a/testimonials/index.txt +++ b/testimonials/index.txt @@ -12,7 +12,7 @@ f:I[6213,[],"ViewportBoundary"] 1:HL["/_next/static/media/4473ecc91f70f139-s.p.woff","font",{"crossOrigin":"","type":"font/woff"}] 2:HL["/_next/static/media/463dafcda517f24f-s.p.woff","font",{"crossOrigin":"","type":"font/woff"}] 3:HL["/_next/static/css/8043a7c984777fb1.css","style"] -0:{"P":null,"b":"ZNcQrWMYk7ymGN9y8PjnQ","p":"","c":["","testimonials",""],"i":false,"f":[[["",{"children":["testimonials",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$4","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L5",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L8","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L8","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L8","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L8","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$L9",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["testimonials",["$","$4","c",{"children":[null,["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children","testimonials","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]]}],{"children":["__PAGE__",["$","$4","c",{"children":[["$","div",null,{"className":"container mx-auto px-4 py-12","children":[["$","h1",null,{"className":"text-3xl md:text-4xl font-medium text-center mb-12","children":"Community Feedback"}],["$","$La",null,{}]]}],null,["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null]},null],["$","$4","h",{"children":[null,["$","$4","6qnBv18VhTET2FzHGeXfO",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$11","$undefined"],"s":false,"S":true} +0:{"P":null,"b":"vJuFcpWvbN6zbAub4gcIZ","p":"","c":["","testimonials",""],"i":false,"f":[[["",{"children":["testimonials",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$4","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/8043a7c984777fb1.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","children":[["$","head",null,{"children":[["$","link",null,{"rel":"shortcut icon","href":"/albumentations_logo.png"}],["$","link",null,{"rel":"stylesheet","href":"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"}]]}],["$","body",null,{"className":"__variable_1e4310 __variable_c3aa02 font-sans antialiased","children":[["$","$L5",null,{}],["$","main",null,{"className":"pt-16","children":["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}],["$","footer",null,{"className":"border-t","children":["$","div",null,{"className":"container mx-auto px-4 py-8","children":["$","div",null,{"className":"flex flex-col md:flex-row justify-between items-start md:items-center gap-6","children":[["$","nav",null,{"className":"flex flex-wrap gap-x-6 gap-y-2","children":[["$","$L8","/",{"href":"/","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Home"}],["$","$L8","/docs",{"href":"/docs","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Documentation"}],["$","$L8","/whos_using",{"href":"/whos_using","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Who's using"}],["$","a","https://explore.albumentations.ai",{"href":"https://explore.albumentations.ai","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"Explore"}],["$","$L8","/people",{"href":"/people","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"People"}],["$","a","https://github.com/albumentations-team/albumentations",{"href":"https://github.com/albumentations-team/albumentations","target":"_blank","rel":"noopener noreferrer","className":"text-gray-600 hover:text-gray-900 transition-colors","children":"GitHub"}]]}],["$","a",null,{"href":"https://github.com/sponsors/albumentations-team","className":"inline-flex items-center justify-center font-medium transition-colors rounded-md border-2 border-green-600 text-green-600 hover:bg-green-50 px-4 py-2","target":"_blank","rel":"noopener noreferrer","children":["$undefined","Sponsor"]}]]}]}]}]]}],["$","$L9",null,{"gaId":"G-DCXRDR9HJ0"}]]}]]}],{"children":["testimonials",["$","$4","c",{"children":[null,["$","$L6",null,{"parallelRouterKey":"children","segmentPath":["children","testimonials","children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined"}]]}],{"children":["__PAGE__",["$","$4","c",{"children":[["$","div",null,{"className":"container mx-auto px-4 py-12","children":[["$","h1",null,{"className":"text-3xl md:text-4xl font-medium text-center mb-12","children":"Community Feedback"}],["$","$La",null,{}]]}],null,["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null]},null],["$","$4","h",{"children":[null,["$","$4","w-XkCD44dyevLpF2uef-4",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":["$11","$undefined"],"s":false,"S":true} 10:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] e:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"Community Testimonials | Albumentations"}],["$","meta","2",{"name":"description","content":"Real feedback from the Albumentations community"}],["$","meta","3",{"name":"robots","content":"index, follow"}],["$","link","4",{"rel":"canonical","href":"https://albumentations.ai/"}],["$","meta","5",{"property":"og:title","content":"Community Testimonials"}],["$","meta","6",{"property":"og:description","content":"Real feedback from the Albumentations community"}],["$","meta","7",{"property":"og:url","content":"https://albumentations.ai/"}],["$","meta","8",{"property":"og:site_name","content":"Albumentations"}],["$","meta","9",{"property":"og:locale","content":"en_US"}],["$","meta","10",{"property":"og:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","meta","11",{"property":"og:image:width","content":"1200"}],["$","meta","12",{"property":"og:image:height","content":"630"}],["$","meta","13",{"property":"og:image:alt","content":"Albumentations"}],["$","meta","14",{"property":"og:type","content":"website"}],["$","meta","15",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","16",{"name":"twitter:site","content":"@albumentations"}],["$","meta","17",{"name":"twitter:creator","content":"@viglovikov"}],["$","meta","18",{"name":"twitter:title","content":"Community Testimonials"}],["$","meta","19",{"name":"twitter:description","content":"Real feedback from the Albumentations community"}],["$","meta","20",{"name":"twitter:image","content":"https://albumentations.ai/assets/albumentations_card.png"}],["$","link","21",{"rel":"icon","href":"/icon.svg?ed95530d83f93aed","type":"image/svg+xml","sizes":"any"}]] c:null