
    h^)                      S r SSKJr  SSKrSSKJrJrJrJr  SSK	J
r
  SSKrSSKrSSKJrJrJr  SSKJrJrJrJrJr  SSKJr  SS	KJr  SS
KJrJrJr  SSK J!r!J"r"J#r#J$r$  SSK%J&r&J'r'  SSK(J)r)  SSK*J+r+  SSK,J-r.  / SQr/Sr0Sr1 " S S\'5      r2 " S S\'5      r3 " S S\35      r4 " S S\'5      r5 " S S\'5      r6 " S S\'5      r7g) a  Geometric transformation classes for image augmentation.

This module provides a collection of transforms that modify the geometric properties
of images and associated data (masks, bounding boxes, keypoints). Includes implementations
for flipping, transposing, affine transformations, distortions, padding, and more complex
transformations like grid shuffling and thin plate splines.
    )annotationsN)	AnnotatedAnyLiteralcast)warn)batch_transformis_grayscale_imageis_rgb_image)AfterValidatorFieldValidationInfofield_validatormodel_validator)Self)check_range)BboxProcessordenormalize_bboxesnormalize_bboxes)NonNegativeFloatRangeTypeOnePlusIntRangeTypeSymmetricRangeTypecheck_range_bounds)BaseTransformInitSchemaDualTransform)ALL_TARGETSto_tuple   )
functional)AffineGridElasticDeformMorphologicalPerspectiveRandomGridShuffleShiftScaleRotate      c            	        ^  \ rS rSrSr\r " S S\5      rSSS\	R                  \	R                  \	R                  SSS	4	                 SU 4S
 jjjr            SS jr\" SSSS9SS j5       r\" SSSS9SS j5       r\" SSSS9SS j5       r\" SSSS9SS j5       r            SS jr            SS jr            SS jr      S S jrSrU =r$ )!r$   =   a  Apply random four point perspective transformation to the input.

Args:
    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 (tuple[float, ...] | float): Padding value if border_mode is cv2.BORDER_CONSTANT.
        Default: 0.
    fill_mask (tuple[float, ...] | float): 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:
    >>> import numpy as np
    >>> import albumentations as A
    >>> import cv2
    >>>
    >>> # Prepare sample data
    >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
    >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
    >>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
    >>> bbox_labels = [1, 2]
    >>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
    >>> keypoint_labels = [0, 1]
    >>>
    >>> # Define transform with parameters as tuples when possible
    >>> transform = A.Compose([
    ...     A.Perspective(
    ...         scale=(0.05, 0.1),
    ...         keep_size=True,
    ...         fit_output=False,
    ...         border_mode=cv2.BORDER_CONSTANT,
    ...         p=1.0
    ...     ),
    ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['bbox_labels']),
    ...    keypoint_params=A.KeypointParams(format='xy', label_fields=['keypoint_labels']))
    >>>
    >>> # Apply the transform
    >>> transformed = transform(
    ...     image=image,
    ...     mask=mask,
    ...     bboxes=bboxes,
    ...     bbox_labels=bbox_labels,
    ...     keypoints=keypoints,
    ...     keypoint_labels=keypoint_labels
    ... )
    >>>
    >>> # Get the transformed data
    >>> transformed_image = transformed['image']      # Perspective-transformed image
    >>> transformed_mask = transformed['mask']        # Perspective-transformed mask
    >>> transformed_bboxes = transformed['bboxes']    # Perspective-transformed bounding boxes
    >>> transformed_bbox_labels = transformed['bbox_labels']  # Labels for transformed bboxes
    >>> transformed_keypoints = transformed['keypoints']  # Perspective-transformed keypoints
    >>> transformed_keypoint_labels = transformed['keypoint_labels']  # Labels for transformed keypoints

c                  f    \ rS rSr% S\S'   S\S'   S\S'   S\S'   S\S	'   S
\S'   S
\S'   S\S'   Srg)Perspective.InitSchema   r   scalebool	keep_size
fit_outputaLiteral[cv2.INTER_NEAREST, cv2.INTER_LINEAR, cv2.INTER_CUBIC, cv2.INTER_AREA, cv2.INTER_LANCZOS4]interpolationmask_interpolationtuple[float, ...] | floatfill	fill_maskoLiteral[cv2.BORDER_CONSTANT, cv2.BORDER_REPLICATE, cv2.BORDER_REFLECT, cv2.BORDER_WRAP, cv2.BORDER_REFLECT_101]border_mode N__name__
__module____qualname____firstlineno____annotations____static_attributes__r:       k/var/www/fran/franai/venv/lib/python3.13/site-packages/albumentations/augmentations/geometric/transforms.py
InitSchemar,      s?    ((
 	

 	
 (',,
 	
rB   rD   )g?皙?TFr         ?c
                   > [         T
U ]  U	5        [        SU5      U l        X l        X`l        Xpl        Xl        X0l        X@l	        XPl
        g )Ntuple[float, float])super__init__r   r.   r0   r9   r6   r7   r1   r3   r4   )selfr.   r0   r1   r3   r4   r9   r6   r7   p	__class__s             rC   rJ   Perspective.__init__   sG    < 	/7
"&	"$*"4rB   c           
         [         R                  " UUUUU R                  U R                  U R                  U R
                  5      $ )aL  Apply the perspective transform to an image.

Args:
    img (np.ndarray): Image to be distorted.
    matrix (np.ndarray): Transformation matrix.
    max_height (int): Maximum height of the image.
    max_width (int): Maximum width of the image.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted image.

)
fgeometricperspectiver6   r9   r0   r3   )rK   imgmatrix
max_height	max_widthparamss         rC   applyPerspective.apply   sC    * %%IINN	
 		
rB   spatialhas_batch_dimhas_depth_dimc                (    U R                   " U40 UD6$ )zApply the perspective transform to a batch of images.

Args:
    images (np.ndarray): Batch of images to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Batch of distorted images.

rW   rK   imagesrV   s      rC   apply_to_imagesPerspective.apply_to_images        zz&+F++rB   c                (    U R                   " U40 UD6$ )zApply the perspective transform to a volume.

Args:
    volume (np.ndarray): Volume to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted volume.

r^   rK   volumerV   s      rC   apply_to_volumePerspective.apply_to_volume  rc   rB   c                (    U R                   " U40 UD6$ )zApply the perspective transform to a batch of volumes.

Args:
    volumes (np.ndarray): Batch of volumes to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Batch of distorted volumes.

r^   rK   volumesrV   s      rC   apply_to_volumesPerspective.apply_to_volumes       zz',V,,rB   c                (    U R                   " U40 UD6$ )zApply the perspective transform to a 3D mask.

Args:
    mask3d (np.ndarray): 3D mask to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted 3D mask.

apply_to_maskrK   mask3drV   s      rC   apply_to_mask3dPerspective.apply_to_mask3d'       !!&3F33rB   c           
         [         R                  " UUUUU R                  U R                  U R                  U R
                  5      $ )aG  Apply the perspective transform to a mask.

Args:
    mask (np.ndarray): Mask to be distorted.
    matrix (np.ndarray): Transformation matrix.
    max_height (int): Maximum height of the mask.
    max_width (int): Maximum width of the mask.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted mask.

)rP   rQ   r7   r9   r0   r4   )rK   maskrS   rT   rU   rV   s         rC   rq   Perspective.apply_to_mask5  sC    * %%NNNN##	
 		
rB   c                R    [         R                  " UUS   UUUU R                  5      $ )a  Apply the perspective transform to a batch of bounding boxes.

Args:
    bboxes (np.ndarray): Batch of bounding boxes to be distorted.
    matrix_bbox (np.ndarray): Transformation matrix.
    max_height (int): Maximum height of the bounding boxes.
    max_width (int): Maximum width of the bounding boxes.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Batch of distorted bounding boxes.

shape)rP   perspective_bboxesr0   )rK   bboxesmatrix_bboxrT   rU   rV   s         rC   apply_to_bboxesPerspective.apply_to_bboxesU  s1    * ,,7ONN
 	
rB   c                R    [         R                  " UUS   UUUU R                  5      $ )a  Apply the perspective transform to a batch of keypoints.

Args:
    keypoints (np.ndarray): Batch of keypoints to be distorted.
    matrix (np.ndarray): Transformation matrix.
    max_height (int): Maximum height of the keypoints.
    max_width (int): Maximum width of the keypoints.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Batch of distorted keypoints.

r{   )rP   perspective_keypointsr0   )rK   	keypointsrS   rT   rU   rV   s         rC   apply_to_keypointsPerspective.apply_to_keypointss  s1    * //7ONN
 	
rB   c                `   US   SS nU R                   R                  " U R                  6 n[        R                  " UUU R
                  5      n[        R                  " U5      n[        R                  " UU5      u  pgnU R                  (       a  [        R                  " UU5      u  pgnUUUUS.$ )Get the parameters dependent on the data.

Args:
    params (dict[str, Any]): Parameters.
    data (dict[str, Any]): Data.

Returns:
    dict[str, Any]: Parameters.

r{   Nr'   )rS   rT   rU   r~   )
	py_randomuniformr.   rP   generate_perspective_pointsrandom_generatororder_pointscompute_perspective_paramsr1   expand_transform)	rK   rV   dataimage_shaper.   pointsrS   rU   rT   s	            rC   get_params_dependent_on_data(Perspective.get_params_dependent_on_data  s     Wobq)&&

377!!

 ((0(2(M(M)
%:
 ??,6,G,G-)Fz $"!	
 	
rB   )r9   r6   r7   r1   r3   r0   r4   r.   )r.   tuple[float, float] | floatr0   r/   r1   r/   r3   r2   r4   r2   r9   r8   r6   r5   r7   r5   rL   float)rR   
np.ndarrayrS   r   rT   intrU   r   rV   r   returnr   r`   r   rV   r   r   r   rf   r   rV   r   r   r   rk   r   rV   r   r   r   rs   r   rV   r   r   r   )rx   r   rS   r   rT   r   rU   r   rV   r   r   r   )r}   r   r~   r   rT   r   rU   r   rV   r   r   r   )r   r   rS   r   rT   r   rU   r   rV   r   r   r   rV   dict[str, Any]r   r   r   r   )r<   r=   r>   r?   __doc__r   _targetsr   rD   cv2INTER_LINEARINTER_NEARESTBORDER_CONSTANTrJ   rW   r	   ra   rg   rl   rt   rq   r   r   r   rA   __classcell__rM   s   @rC   r$   r$   =   sR   Wr H
, 
< .9    *+/09&5*&5 &5 	&5

&5
&5&
'&54 (5&56 -7&58 9&5 &5P

 
 	

 
 
 

@ Yd%H, I, Ye4H, I, Yd$G- H- Yd%H4 I4

 
 	

 
 
 

@

  
 	

 
 
 

<

 
 	

 
 
 

<)
)
 )
 
	)
 )
rB   r$   c                  ,  ^  \ rS rSrSr\r " S S\5      rSSSSS\	R                  \	R                  S	S	S
S	\	R                  SSS4                             SU 4S jjjr          SS jr          S S jr          S!S jr          S"S jr\" SSS	S9S#S j5       r\" SS	SS9S$S j5       r\" SSSS9S%S j5       r\" SSS	S9S&S j5       r\          S'S j5       r      S(S jrS)S jrS*S jrSrU =r$ )+r!   i  a(  Augmentation to apply affine transformations to images.

Affine transformations involve:

    - Translation ("move" image on the x-/y-axis)
    - Rotation
    - Scaling ("zoom" in/out)
    - Shear (move one side of the image, turning a square into a trapezoid)

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.

Args:
    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 (tuple[float, ...] | float): 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 (tuple[float, ...] | float): 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

References:
    Towards Rotation Invariance in Object Detection: https://arxiv.org/abs/2109.13488

Examples:
    >>> import numpy as np
    >>> import albumentations as A
    >>> import cv2
    >>>
    >>> # Prepare sample data
    >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
    >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
    >>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
    >>> bbox_labels = [1, 2]
    >>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
    >>> keypoint_labels = [0, 1]
    >>>
    >>> # Define transform with different parameter types
    >>> transform = A.Compose([
    ...     A.Affine(
    ...         # Tuple for scale (will be used for both x and y)
    ...         scale=(0.8, 1.2),
    ...         # Dictionary with tuples for different x/y translations
    ...         translate_percent={"x": (-0.2, 0.2), "y": (-0.1, 0.1)},
    ...         # Tuple for rotation range
    ...         rotate=(-30, 30),
    ...         # Dictionary with tuples for different x/y shearing
    ...         shear={"x": (-10, 10), "y": (-5, 5)},
    ...         # Interpolation methods
    ...         interpolation=cv2.INTER_LINEAR,
    ...         mask_interpolation=cv2.INTER_NEAREST,
    ...         # Other parameters
    ...         fit_output=False,
    ...         keep_ratio=True,
    ...         rotate_method="largest_box",
    ...         balanced_scale=True,
    ...         border_mode=cv2.BORDER_CONSTANT,
    ...         fill=0,
    ...         fill_mask=0,
    ...         p=1.0
    ...     ),
    ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['bbox_labels']),
    ...    keypoint_params=A.KeypointParams(format='xy', label_fields=['keypoint_labels']))
    >>>
    >>> # Apply the transform
    >>> transformed = transform(
    ...     image=image,
    ...     mask=mask,
    ...     bboxes=bboxes,
    ...     bbox_labels=bbox_labels,
    ...     keypoints=keypoints,
    ...     keypoint_labels=keypoint_labels
    ... )
    >>>
    >>> # Get the transformed data
    >>> transformed_image = transformed['image']      # Image with affine transforms applied
    >>> transformed_mask = transformed['mask']        # Mask with affine transforms applied
    >>> transformed_bboxes = transformed['bboxes']    # Bounding boxes with affine transforms applied
    >>> transformed_bbox_labels = transformed['bbox_labels']  # Labels for transformed bboxes
    >>> transformed_keypoints = transformed['keypoints']  # Keypoints with affine transforms applied
    >>> transformed_keypoint_labels = transformed['keypoint_labels']  # Labels for transformed keypoints
    >>>
    >>> # Simpler example with only essential parameters
    >>> simple_transform = A.Compose([
    ...     A.Affine(
    ...         scale=1.1,  # Single scalar value for scale
    ...         rotate=15,  # Single scalar value for rotation (degrees)
    ...         translate_px=30,  # Single scalar value for translation (pixels)
    ...         p=1.0
    ...     ),
    ... ])
    >>> simple_result = simple_transform(image=image)
    >>> simple_transformed = simple_result['image']

c                  P   \ rS rSr% S\S'   S\S'   S\S'   S\S'   S\S	'   S
\S'   S
\S'   S\S'   S\S'   S\S'   S\S'   S\S'   S\S'   S\S'   \" S	S5      \      S S j5       5       r\" S5      \    S!S j5       5       r\	" SS9S"S j5       r
\ S#       S$S jj5       rSrg)%Affine.InitSchemaio  Dtuple[float, float] | float | dict[str, float | tuple[float, float]]r.   Ktuple[float, float] | float | dict[str, float | tuple[float, float]] | Nonetranslate_percenttranslate_pxr   rotateshearr2   r3   r4   r5   r6   r7   r8   r9   r/   r1   
keep_ratio!Literal['largest_box', 'ellipse']rotate_methodbalanced_scalec                8    U R                  XR                  5      $ N)_handle_dict_arg
field_name)clsvalueinfos      rC   _process_shear Affine.InitSchema._process_shear  s     ''??rB   c                    [        X5      $ r   r   )r   r   s     rC   _process_rotate!Affine.InitSchema._process_rotate  s     E))rB   aftermodec                J   U R                   c  U R                  c  SU l        U R                   b  U R                  b  Sn[        U5      eU R                   b   U R                  U R                   SSS9U l         U R                  b   U R                  U R                  SSS9U l        U $ )Nr   zYExpected either translate_percent or translate_px to be provided, but both were provided.r           )defaultr   )r   r   
ValueErrorr   )rK   msgs     rC   _handle_translate#Affine.InitSchema._handle_translate  s    %%-$2C2C2K$%!%%1d6G6G6Sq o%%%1)-)>)>**' *? *&   ,$($9$9%%" %: %! KrB   c                >   [        U [        5      (       a  X 4X 4S.$ [        U [        5      (       aV  SU ;  a  SU ;  a  [        SU S35      eU R	                  SU5      nU R	                  SU5      n[        X35      [        XD5      S.$ [        X 5      [        X 5      S.$ )Nxyr   r   z	Expected zJ dictionary to contain at least key "x" or key "y". Found neither of them.)
isinstancer   dictr   getr   )valnamer   r   r   s        rC   r   "Affine.InitSchema._handle_dict_arg  s     #u%%!Zsj99#t$$c>cn$#D6)st  GGC)GGC)%a^(1.AA!#+(32DEErB   )r   r   N)r   r   r   r   r   dict[str, tuple[float, float]])r   r   r   rH   r   r   )      ?)r   zytuple[float, float] | dict[str, float | tuple[float, float]] | float | tuple[int, int] | dict[str, int | tuple[int, int]]r   z
str | Noner   r   r   r   )r<   r=   r>   r?   r@   r   classmethodr   r   r   r   staticmethodr   rA   r:   rB   rC   rD   r   o  s;   SSffaa++SS
 	

 	
 (',,
 	
 88	'	*		@W	@ !	@ ,		@ 
 
+	@ 
	"		*.	* !	* 
 
#	* 
g	&	 
'	0 
 !	F/	F 	F 	F ,	F 
	FrB   rD   )r   r   Nr   )r   r   Flargest_boxr   rF   c                  > [         TU ]  US9  X`l        Xpl        Xl        Xl        Xl        [        SU5      U l        [        SU5      U l	        [        SU5      U l
        [        SU5      U l        Xl        [        SU5      U l        Xl        Xl        Xl        U R                  (       a9  U R                  S   U R                  S   :w  a  [#        SU R                   35      eg g )NrL   r   zdict[str, tuple[int, int]]rH   r   r   zJWhen keep_ratio is True, the x and y scale range should be identical. got )rI   rJ   r3   r4   r6   r7   r9   r   r.   r   r   r   r1   r   r   r   r   r   )rK   r.   r   r   r   r   r3   r4   r1   r   r   r   r9   r6   r7   rL   rM   s                   rC   rJ   Affine.__init__  s    H 	1*"4	"&:EB
!%&FHY!Z !=|L0&9$:EB
$*,??tzz#$**S/A\]a]g]g\hi   B?rB   c           	     p    [         R                  " UUU R                  U R                  U R                  US9$ )a  Apply the affine transform to an image.

Args:
    img (np.ndarray): Image to be distorted.
    matrix (np.ndarray): Transformation matrix.
    output_shape (tuple[int, int]): Output shape.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted image.

r3   r6   r9   output_shape)rP   warp_affiner3   r6   r9   )rK   rR   rS   r   rV   s        rC   rW   Affine.apply  s9    & %%,,((%
 	
rB   c           	     p    [         R                  " UUU R                  U R                  U R                  US9$ )a  Apply the affine transform to a mask.

Args:
    mask (np.ndarray): Mask to be distorted.
    matrix (np.ndarray): Transformation matrix.
    output_shape (tuple[int, int]): Output shape.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted mask.

r   )rP   r   r4   r7   r9   )rK   rx   rS   r   rV   s        rC   rq   Affine.apply_to_mask)  s9    & %%11((%
 	
rB   c                l    [         R                  " UUU R                  US   SS U R                  U5      $ )a5  Apply the affine transform to bounding boxes.

Args:
    bboxes (np.ndarray): Bounding boxes to be distorted.
    bbox_matrix (np.ndarray): Transformation matrix.
    output_shape (tuple[int, int]): Output shape.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted bounding boxes.

r{   Nr'   )rP   bboxes_affiner   r9   )rK   r}   bbox_matrixr   rV   s        rC   r   Affine.apply_to_bboxesE  s@    & ''7OBQ
 	
rB   c                P    [         R                  " UUUS   UU R                  5      $ )a  Apply the affine transform to keypoints.

Args:
    keypoints (np.ndarray): Keypoints to be distorted.
    matrix (np.ndarray): Transformation matrix.
    scale (dict[str, float]): Scale.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted keypoints.

r{   )rP   keypoints_affiner9   )rK   r   rS   r.   rV   s        rC   r   Affine.apply_to_keypointsa  s0    & **7O
 	
rB   rY   TrZ   c                (    U R                   " U40 UD6$ )zApply the affine transform to a batch of images.

Args:
    images (np.ndarray): Images to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted images.

r^   r_   s      rC   ra   Affine.apply_to_images|  rc   rB   c                (    U R                   " U40 UD6$ )zApply the affine transform to a volume.

Args:
    volume (np.ndarray): Volume to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted volume.

r^   re   s      rC   rg   Affine.apply_to_volume  rc   rB   c                (    U R                   " U40 UD6$ )zApply the affine transform to a batch of volumes.

Args:
    volumes (np.ndarray): Volumes to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted volumes.

r^   rj   s      rC   rl   Affine.apply_to_volumes  rn   rB   c                (    U R                   " U40 UD6$ )zApply the affine transform to a 3D mask.

Args:
    mask3d (np.ndarray): 3D mask to be distorted.
    **params (Any): Additional parameters.

Returns:
    np.ndarray: Distorted 3D mask.

rp   rr   s      rC   rt   Affine.apply_to_mask3d  rv   rB   c                   0 nU R                  5        H  u  pV[        U[        [        45      (       a  [        U5      XE'   M/  [        U[        5      (       a|  U(       ab  US   S:  a  US   S4OS nUS   S:  a  SUS   4OS nUb  Ub  UR                  Xx/5      n	OUb  Un	OUb  Un	OSXE'   M  UR                  " U	6 XE'   M  UR                  " U6 XE'   M  [        SU SU S35      e   U(       a  US   US'   U$ )	Nr   r   r   zInvalid scale value for key z: z,. Expected a float or a tuple of two floats.r   r   )itemsr   r   r   tuplechoicer   	TypeError)
r.   r   r   random_stateresult_scalekeyr   lower_intervalupper_intervalselected_intervals
             rC   
_get_scaleAffine._get_scale  s"    ++-JC%#u..$)%L!E5))!8=a1eAh_$N8=a1c58_$N%1n6P,8,?,?+<-) (3,:)'3,:),/) (4(<(<>O(PL%(4(<(<e(DL%23%r%@lm 1 (8  ,S 1LrB   c                n   US   SS nU R                  U5      nU R                  5       nU R                  U R                  U R                  U R
                  U R                  5      nU R                  R                  " U R                  6 n[        R                  " U5      n[        R                  " U5      n	[        R                  " UUUUU5      n
[        R                  " UUUUU	5      nU R                  (       a3  [        R                  " U
U5      u  p[        R                  " UU5      u  pOUnUUU
UUS.$ )r   r{   Nr'   )r   r.   rS   r   r   )_get_translate_params_get_shear_paramsr   r.   r   r   r   r   r   rP   centercenter_bbox#create_affine_transformation_matrixr1    compute_affine_warp_output_shape)rK   rV   r   r   	translater   r.   r   image_shift
bbox_shiftrS   r   r   _s                 rC   r   #Affine.get_params_dependent_on_data  s8    Wobq)..{;	&&(JJOONN	
 ''5 ''4++K8
??
 !DD
 ??#-#N#N$ F (HHNK
 'L &(
 	
rB   c                   US S u  p#U R                   b  U R                  R                  [        U R                   S   S   5      [        U R                   S   S   5      5      U R                  R                  [        U R                   S   S   5      [        U R                   S   S   5      5      S.$ U R                  br  U R                  R                  5        VVs0 s H  u  pEX@R                  R                  " U6 _M      nnn[        S[        US   U-  5      [        US   U-  5      S.5      $ [        SSSS.5      $ s  snnf )Nr'   r   r   r   r   r   dict[str, int])r   r   randintr   r   r   r   r   )rK   r   heightwidthr   r   r  s          rC   r  Affine._get_translate_params  s=   #BQ(^^++C0A0A#0Fq0I,JCPTPaPabePfghPiLjk^^++C0A0A#0Fq0I,JCPTPaPabePfghPiLjk  !!-OSOeOeOkOkOmnOmnn44e<<OmIn )C.501IcNV<S8TU  $AA&677 os   %E c                    U R                   R                  " U R                  S   6 * U R                   R                  " U R                  S   6 * S.$ )Nr   r   r   )r   r   r   rK   s    rC   r  Affine._get_shear_params-  sF    ..(($**S/::..(($**S/::
 	
rB   )r   r9   r6   r7   r1   r3   r   r4   r   r   r.   r   r   r   )r.   r   r   r   r   z?tuple[int, int] | int | dict[str, int | tuple[int, int]] | Noner   r   r   r   r3   r2   r4   r2   r1   r/   r   r/   r   r   r   r/   r9   r8   r6   r5   r7   r5   rL   r   )
rR   r   rS   r   r   tuple[int, int]rV   r   r   r   )
rx   r   rS   r   r   r  rV   r   r   r   )
r}   r   r   r   r   r  rV   r   r   r   )
r   r   rS   r   r.   dict[str, float]rV   r   r   r   r   r   r   r   )
r.   r   r   r/   r   r/   r   zrandom.Randomr   r  r   )r   r  r   r  r   r  )r<   r=   r>   r?   r   r   r   r   rD   r   r   r   r   rJ   rW   rq   r   r   r	   ra   rg   rl   rt   r   r   r   r  r  rA   r   r   s   @rC   r!   r!     s   m^ HbF, bFL WaimX\.1V`    ;H$ *+/0E8S8 g8 V	8
 ,8 T8
8
8* +8, -8. 9/80 182
38@ (A8B -C8D E8 8t

 
 &	

 
 

8

 
 &	

 
 

8

  
 &	

 
 

8

 
  	

 
 

6 Yd%H, I, Ye4H, I, Yd$G- H- Yd%H4 I4 &-&& & $	&
 
& &P?
?
 ?
 
	?
B8
 
rB   r!   c                     ^  \ rS rSrSr\r " S S\5      rSSS\	R                  \	R                  SSS	\	R                  S
S
S4                       SU 4S jjjrSS jrSrU =r$ )r&   i4  a"  Randomly apply affine transforms: translate, scale and rotate the input.

Args:
    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_CONSTANT
    fill (tuple[float, ...] | float): padding value if border_mode is cv2.BORDER_CONSTANT.
    fill_mask (tuple[float, ...] | float): 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

Examples:
    >>> import numpy as np
    >>> import albumentations as A
    >>> import cv2
    >>>
    >>> # Prepare sample data
    >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
    >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
    >>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
    >>> bbox_labels = [1, 2]
    >>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
    >>> keypoint_labels = [0, 1]
    >>>
    >>> # Define transform with parameters as tuples when possible
    >>> transform = A.Compose([
    ...     A.ShiftScaleRotate(
    ...         shift_limit=(-0.0625, 0.0625),
    ...         scale_limit=(-0.1, 0.1),
    ...         rotate_limit=(-45, 45),
    ...         interpolation=cv2.INTER_LINEAR,
    ...         border_mode=cv2.BORDER_CONSTANT,
    ...         rotate_method="largest_box",
    ...         p=1.0
    ...     ),
    ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['bbox_labels']),
    ...    keypoint_params=A.KeypointParams(format='xy', label_fields=['keypoint_labels']))
    >>>
    >>> # Apply the transform
    >>> transformed = transform(
    ...     image=image,
    ...     mask=mask,
    ...     bboxes=bboxes,
    ...     bbox_labels=bbox_labels,
    ...     keypoints=keypoints,
    ...     keypoint_labels=keypoint_labels
    ... )
    >>>
    >>> # Get the transformed data
    >>> transformed_image = transformed['image']      # Shifted, scaled and rotated image
    >>> transformed_mask = transformed['mask']        # Shifted, scaled and rotated mask
    >>> transformed_bboxes = transformed['bboxes']    # Shifted, scaled and rotated bounding boxes
    >>> transformed_bbox_labels = transformed['bbox_labels']  # Labels for transformed bboxes
    >>> transformed_keypoints = transformed['keypoints']  # Shifted, scaled and rotated keypoints
    >>> transformed_keypoint_labels = transformed['keypoint_labels']  # Labels for transformed keypoints

c                      \ rS rSr% S\S'   S\S'   S\S'   S\S'   S\S	'   S
\S'   S
\S'   S\S'   S\S'   S\S'   S\S'   \" SS9SS j5       r\" S5      \      SS j5       5       r	Sr
g)ShiftScaleRotate.InitSchemai  r   shift_limitscale_limitrotate_limitr2   r3   r8   r9   r5   r6   r7   "tuple[float, float] | float | Noneshift_limit_xshift_limit_yr   r   r4   r   r   c                6   Sn[        U R                  b  U R                  OU R                  5      U l        [        U R                  /UQSP76   [        U R                  b  U R                  OU R                  5      U l        [        U R                  /UQSP76   U $ )N)r   r  r   )r   r  r  r   r   )rK   boundss     rC   _check_shift_limit.ShiftScaleRotate.InitSchema._check_shift_limit  s    F!)&*&8&8&D""$JZJZ"D **EVE_E!)&*&8&8&D""$JZJZ"D **EVE_EKrB   c                v    S[        S5      4n[        USS9n[        U/UQ[        UR                  5      P76   U$ )Nr   infr   bias)r   r   r   strr   )r   r   r   r#  results        rC   _check_scale_limit.ShiftScaleRotate.InitSchema._check_scale_limit  s=     e_Fe#.F>>T__)=>MrB   )r  r   Nr   )r   r   r   r   r   rH   )r<   r=   r>   r?   r@   r   r$  r   r   r,  rA   r:   rB   rC   rD   r    s    ''''((
 	

 	
 (',,999988
 	
 
g	&	 
'	 
	'		.	 !	 !		 
 
(	rB   rD   )g      g      ?)grE   )i-   Nr   r   rF   c                
  > [        SU5      n[        SU5      n[        TU ]	  UXgS.USUU	U
UUSSUUS9  [        S[        SS9  X`l        Xpl        [        SU5      U l        [        S	U5      U l        XPl	        Xl
        Xl        g )
NrH   r   )r   r   F)r.   r   r   r   r3   r4   r6   r7   r9   r1   r   r   rL   z\ShiftScaleRotate is a special case of Affine transform. Please use Affine transform instead.r'   )
stacklevelr  )r   rI   rJ   r   UserWarningr  r   r  r  r9   r6   r7   )rK   r  r  r  r3   r9   r  r   r   r4   r6   r7   rL   rM   s                rC   rJ   ShiftScaleRotate.__init__  s    6 2MB2MB$1F'1#' 	 	
 	j	

 +* 5{C !2LA&	"rB   c                    U R                   U R                  [        U R                  SS9U R                  U R
                  U R                  U R                  U R                  U R                  U R                  S.
$ )zoGet the transform initialization arguments.

Returns:
    dict[str, Any]: Transform initialization arguments.

g      r(  )
r  r   r  r  r3   r9   r6   r7   r   r4   )r  r   r   r  r  r3   r9   r6   r7   r   r4   r  s    rC   get_transform_init_args(ShiftScaleRotate.get_transform_init_args  sk     "//!//#D$4$44@ --!//++II!//"&"9"9
 	
rB   )r9   r6   r7   r  r  r  r   )r  r   r  r   r  r   r3   r2   r9   r   r  r  r   r  r   r   r4   r2   r6   r5   r7   r5   rL   r   )r   r   )r<   r=   r>   r?   r   r   r   r   rD   r   r   r   r   rJ   r4  rA   r   r   s   @rC   r&   r&   4  s    Vp H:, :| 4E3>4= ..<@<@;H *+/038#08# 18# 2	8#

8# 8# :8# :8# 98# 
!8#. (/8#0 -18#2 38# 8#t
 
rB   r&   c                     ^  \ rS rSrSr\r " S S\5      r\	R                  \	R                  S4         SU 4S jjjr\SS j5       r      SS jr        SS	 jr        SS
 jr        SS jr        SS jrSrU =r$ )r"   i  aM  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.


Args:
    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

Number of channels:
    1, 3

Examples:
    >>> import numpy as np
    >>> import albumentations as A
    >>> import cv2
    >>>
    >>> # Prepare sample data
    >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
    >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
    >>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
    >>> bbox_labels = [1, 2]
    >>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
    >>> keypoint_labels = [0, 1]
    >>>
    >>> # Define transform with parameters as tuples when possible
    >>> transform = A.Compose([
    ...     A.GridElasticDeform(
    ...         num_grid_xy=(4, 4),
    ...         magnitude=10,
    ...         interpolation=cv2.INTER_LINEAR,
    ...         mask_interpolation=cv2.INTER_NEAREST,
    ...         p=1.0
    ...     ),
    ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['bbox_labels']),
    ...    keypoint_params=A.KeypointParams(format='xy', label_fields=['keypoint_labels']))
    >>>
    >>> # Apply the transform
    >>> transformed = transform(
    ...     image=image,
    ...     mask=mask,
    ...     bboxes=bboxes,
    ...     bbox_labels=bbox_labels,
    ...     keypoints=keypoints,
    ...     keypoint_labels=keypoint_labels
    ... )
    >>>
    >>> # Get the transformed data
    >>> transformed_image = transformed['image']      # Elastically deformed image
    >>> transformed_mask = transformed['mask']        # Elastically deformed mask
    >>> transformed_bboxes = transformed['bboxes']    # Elastically deformed bounding boxes
    >>> transformed_bbox_labels = transformed['bbox_labels']  # Labels for transformed bboxes
    >>> transformed_keypoints = transformed['keypoints']  # Elastically deformed keypoints
    >>> transformed_keypoint_labels = transformed['keypoint_labels']  # Labels for transformed keypoints

Note:
    This transformation is particularly useful for data augmentation in medical imaging
    and other domains where elastic deformations can simulate realistic variations.

c                  J    \ rS rSr% S\S'   \" SS9rS\S'   S\S	'   S\S
'   Srg)GridElasticDeform.InitSchemaih  GAnnotated[tuple[int, int], AfterValidator(check_range_bounds(1, None))]num_grid_xyr   )gtr   	magnituder2   r3   r4   r:   N)r<   r=   r>   r?   r@   r   r<  rA   r:   rB   rC   rD   r8  h  s*    \\!	3$
 	

 	
rB   rD   r   c                P   > [         TU ]  US9  Xl        X l        X0l        X@l        g Nr   )rI   rJ   r:  r<  r3   r4   )rK   r:  r<  r3   r4   rL   rM   s         rC   rJ   GridElasticDeform.__init__z  s,    ( 	1&"*"4rB   c                R    [         R                  " UR                  SS5      U 45      $ )Nr"  r(   )nphstackreshape)polygons
dimensionss     rC   _generate_mesh GridElasticDeform._generate_mesh  s#    yy*,,R3X>??rB   c           
        US   SS n[         R                  " UU R                  U R                  5      n[        R
                  " U Vs/ s H  nUS   US   US   US   /PM     sn5      R                  U R                  SSS2   S-   5      n[         R                  " UU R                  U R                  5      nU R                  Xv5      nS	U0$ s  snf )
r   r{   Nr'   r   r      r"  )r(   generated_mesh)
rP   split_uniform_gridr:  r   rA  arrayrC   generate_distorted_grid_polygonsr<  rF  )	rK   rV   r   r   tilestilerE  rD  rJ  s	            rC   r   .GridElasticDeform.get_params_dependent_on_data  s     Wobq) --!!
 XX " "D GGGG	 "

 'TrT"T)
 	 >>NN!!
 ,,XB .11+s   	Cc                    [        U5      (       d  [        U5      (       d  [        S5      e[        R                  " XU R
                  5      $ )zApply the GridElasticDeform transform to an image.

Args:
    img (np.ndarray): Image to be transformed.
    generated_mesh (np.ndarray): Generated mesh.
    **params (Any): Additional parameters.

zKGridElasticDeform transform is only supported for RGB and grayscale images.)r   r
   r   rP   distort_imager3   )rK   rR   rJ  rV   s       rC   rW   GridElasticDeform.apply  s?     C  );C)@)@jkk''T=O=OPPrB   c                D    [         R                  " XU R                  5      $ )zApply the GridElasticDeform transform to a mask.

Args:
    mask (np.ndarray): Mask to be transformed.
    generated_mesh (np.ndarray): Generated mesh.
    **params (Any): Additional parameters.

)rP   rR  r4   )rK   rx   rJ  rV   s       rC   rq   GridElasticDeform.apply_to_mask  s     ''d>U>UVVrB   c           	         [        XS   SS 5      n[        [        R                  " UUUS   SS 5      US   SS 5      $ )zApply the GridElasticDeform transform to bounding boxes.

Args:
    bboxes (np.ndarray): Bounding boxes to be transformed.
    generated_mesh (np.ndarray): Generated mesh.
    **params (Any): Additional parameters.

r{   Nr'   )r   r   rP   bbox_distort_image)rK   r}   rJ  rV   bboxes_denorms        rC   r   !GridElasticDeform.apply_to_bboxes  sX     +6'?2A3FG))w#
 7OBQ
 	
rB   c                >    [         R                  " UUUS   SS 5      $ )zApply the GridElasticDeform transform to keypoints.

Args:
    keypoints (np.ndarray): Keypoints to be transformed.
    generated_mesh (np.ndarray): Generated mesh.
    **params (Any): Additional parameters.

r{   Nr'   )rP   distort_image_keypoints)rK   r   rJ  rV   s       rC   r   $GridElasticDeform.apply_to_keypoints  s+     117OBQ
 	
rB   )r3   r<  r4   r:  )
r:  r  r<  r   r3   r2   r4   r2   rL   r   )rD  r   rE  r   r   r   r   )rR   r   rJ  r   rV   r   r   r   )rx   r   rJ  r   rV   r   r   r   )r}   r   rJ  r   rV   r   r   r   )r   r   rJ  r   rV   r   r   r   )r<   r=   r>   r?   r   r   r   r   rD   r   r   r   rJ   r   rF  r   rW   rq   r   r   rA   r   r   s   @rC   r"   r"     sd   IV H
, 
8  %5$5 5
	5
5$ %5 54 @ @/2/2 /2 
	/2bQQ #Q 	Q
 
Q$WW #W 	W
 
W 

 #
 	

 

0

 #
 	

 

 
rB   r"   c                  H  ^  \ rS rSrSr " S S\5      r\r  S   SU 4S jjjr	          SS jr
          SS jr          SS jr\" S	S
SS9SS j5       r\" S	SS
S9SS j5       r\" S	S
S
S9SS j5       r\" S	S
SS9SS j5       r      SS jrSrU =r$ )r%   i  a  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.

Args:
    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 (m*n - 1) is generated.
    3. Each cell in the grid is assigned a number from 0 to (m*n - 1) in row-major order.
    4. The cells are then rearranged according to the permutation P.

Examples:
    >>> import numpy as np
    >>> import albumentations as A
    >>> # Prepare sample data
    >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
    >>> mask = np.random.randint(0, 2, (100, 100), dtype=np.uint8)
    >>> bboxes = np.array([[10, 10, 50, 50], [40, 40, 80, 80]], dtype=np.float32)
    >>> bbox_labels = [1, 2]
    >>> keypoints = np.array([[20, 30], [60, 70]], dtype=np.float32)
    >>> keypoint_labels = [0, 1]
    >>>
    >>> # Define transform with grid as a tuple
    >>> transform = A.Compose([
    ...     A.RandomGridShuffle(grid=(3, 3), p=1.0),
    ... ], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['bbox_labels']),
    ...    keypoint_params=A.KeypointParams(format='xy', label_fields=['keypoint_labels']))
    >>>
    >>> # Apply the transform
    >>> transformed = transform(
    ...     image=image,
    ...     mask=mask,
    ...     bboxes=bboxes,
    ...     bbox_labels=bbox_labels,
    ...     keypoints=keypoints,
    ...     keypoint_labels=keypoint_labels
    ... )
    >>>
    >>> # Get the transformed data
    >>> transformed_image = transformed['image']     # Grid-shuffled image
    >>> transformed_mask = transformed['mask']       # Grid-shuffled mask
    >>> transformed_bboxes = transformed['bboxes']   # Grid-shuffled bounding boxes
    >>> transformed_keypoints = transformed['keypoints']  # Grid-shuffled keypoints
    >>>
    >>> # Visualization example with a simpler grid
    >>> simple_image = np.array([
    ...     [1, 1, 1, 2, 2, 2],
    ...     [1, 1, 1, 2, 2, 2],
    ...     [1, 1, 1, 2, 2, 2],
    ...     [3, 3, 3, 4, 4, 4],
    ...     [3, 3, 3, 4, 4, 4],
    ...     [3, 3, 3, 4, 4, 4]
    ... ])
    >>> simple_transform = A.RandomGridShuffle(grid=(2, 2), p=1.0)
    >>> simple_result = simple_transform(image=simple_image)
    >>> simple_transformed = simple_result['image']
    >>> # The result could look like:
    >>> # array([[4, 4, 4, 2, 2, 2],
    >>> #        [4, 4, 4, 2, 2, 2],
    >>> #        [4, 4, 4, 2, 2, 2],
    >>> #        [3, 3, 3, 1, 1, 1],
    >>> #        [3, 3, 3, 1, 1, 1],
    >>> #        [3, 3, 3, 1, 1, 1]])

c                       \ rS rSr% S\S'   Srg)RandomGridShuffle.InitSchemait  r9  gridr:   Nr;   r:   rB   rC   rD   r_  t  s    UUrB   rD   c                ,   > [         TU ]  US9  Xl        g r>  )rI   rJ   r`  )rK   r`  rL   rM   s      rC   rJ   RandomGridShuffle.__init__y  s    
 	1	rB   c                0    [         R                  " XU5      $ )zApply the RandomGridShuffle transform to an image.

Args:
    img (np.ndarray): Image to be transformed.
    tiles (np.ndarray): Tiles to be transformed.
    mapping (list[int]): Mapping of the tiles.
    **params (Any): Additional parameters.

)rP   swap_tiles_on_image)rK   rR   rN  mappingrV   s        rC   rW   RandomGridShuffle.apply  s      --c'BBrB   c           	         US   SS n[        X5      n[        SU R                  S5      5      nUc  U$ [        R                  " UUUUUR
                  R                  UR
                  R                  S9n[        X5      $ )a  Apply the RandomGridShuffle transform to bounding boxes.

Args:
    bboxes (np.ndarray): Bounding boxes to be transformed.
    tiles (np.ndarray): Tiles to be transformed.
    mapping (np.ndarray): Mapping of the tiles.
    **params (Any): Additional parameters.

r{   Nr'   r   r}   )min_areamin_visibility)	r   r   get_processorrP   bboxes_grid_shufflerV   rh  ri  r   )	rK   r}   rN  re  rV   r   rX  	processorbboxes_returneds	            rC   r   !RandomGridShuffle.apply_to_bboxes  s      Wobq)*6?$*<*<X*FG	M$88%%..$++::
  ==rB   c                0    [         R                  " XU5      $ )a  Apply the RandomGridShuffle transform to keypoints.

Args:
    keypoints (np.ndarray): Keypoints to be transformed.
    tiles (np.ndarray): Tiles to be transformed.
    mapping (np.ndarray): Mapping of the tiles.
    **params (Any): Additional parameters.

)rP   swap_tiles_on_keypoints)rK   r   rN  re  rV   s        rC   r   $RandomGridShuffle.apply_to_keypoints  s      11)GLLrB   rY   TFrZ   c                (    U R                   " U40 UD6$ )zApply the RandomGridShuffle transform to a batch of images.

Args:
    images (np.ndarray): Images to be transformed.
    **params (Any): Additional parameters.

r^   r_   s      rC   ra   !RandomGridShuffle.apply_to_images       zz&+F++rB   c                (    U R                   " U40 UD6$ )zApply the RandomGridShuffle transform to a volume.

Args:
    volume (np.ndarray): Volume to be transformed.
    **params (Any): Additional parameters.

r^   re   s      rC   rg   !RandomGridShuffle.apply_to_volume  rt  rB   c                (    U R                   " U40 UD6$ )zApply the RandomGridShuffle transform to a batch of volumes.

Args:
    volumes (np.ndarray): Volumes to be transformed.
    **params (Any): Additional parameters.

r^   rj   s      rC   rl   "RandomGridShuffle.apply_to_volumes  s     zz',V,,rB   c                (    U R                   " U40 UD6$ )zApply the RandomGridShuffle transform to a 3D mask.

Args:
    mask3d (np.ndarray): 3D mask to be transformed.
    **params (Any): Additional parameters.

r^   rr   s      rC   rt   !RandomGridShuffle.apply_to_mask3d  rt  rB   c                    US   SS n[         R                  " UU R                  U R                  5      n[         R                  " U5      n[         R
                  " UU R                  5      nXFS.$ )zGet the parameters dependent on the data.

Args:
    params (dict[str, Any]): Parameters.
    data (dict[str, Any]): Data.

Returns:
    dict[str, np.ndarray]: Parameters.

r{   Nr'   )rN  re  )rP   rK  r`  r   create_shape_groups!shuffle_tiles_within_shape_groups)rK   rV   r   r   original_tilesshape_groupsre  s          rC   r   .RandomGridShuffle.get_params_dependent_on_data  sp     Wobq)#66II!!

 "55nE>>!!

 (<<rB   )r`  ))rI  rI  rF   )r`  r  rL   r   )
rR   r   rN  r   re  z	list[int]rV   r   r   r   )
r}   r   rN  r   re  r   rV   r   r   r   )
r   r   rN  r   re  r   rV   r   r   r   r   r   r   r   )rV   r   r   r   r   zdict[str, np.ndarray])r<   r=   r>   r?   r   r   rD   r   r   rJ   rW   r   r   r	   ra   rg   rl   rt   r   rA   r   r   s   @rC   r%   r%     s   YvV, V H !'  CC C 	C
 C 
C$>> > 	>
 > 
>>MM M 	M
 M 
M$ Yd%H, I, Ye4H, I, Yd$G- H- Yd%H, I,== = 
	= =rB   r%   c                     ^  \ rS rSrSr\r " S S\5      r   S     SU 4S jjjr	        SS jr
        SS jr      SS jrSS	 jrS
rU =r$ )r#   i  a?  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.

Args:
    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, optional): The probability of applying this transformation. Default is 0.5.

Targets:
    image, mask, keypoints, bboxes, volume, mask3d

Image types:
    uint8, float32

References:
    Nougat: https://github.com/facebookresearch/nougat

Examples:
    >>> import numpy as np
    >>> import albumentations as A
    >>> import cv2
    >>>
    >>> # Create a document-like binary image with text
    >>> image = np.ones((200, 500), dtype=np.uint8) * 255  # White background
    >>> # Add some "text" (black pixels)
    >>> cv2.putText(image, "Document Text", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, 0, 2)
    >>> # Add some "noise" (small black dots)
    >>> for _ in range(50):
    ...     x, y = np.random.randint(0, image.shape[1]), np.random.randint(0, image.shape[0])
    ...     cv2.circle(image, (x, y), 1, 0, -1)
    >>>
    >>> # Create a mask representing text regions
    >>> mask = np.zeros_like(image)
    >>> mask[image < 128] = 1  # Binary mask where text exists
    >>>
    >>> # Example 1: Apply dilation to thicken text and fill gaps
    >>> dilation_transform = A.Morphological(
    ...     scale=3,               # Size of the structuring element
    ...     operation="dilation",  # Expand white regions (or black if inverted)
    ...     p=1.0                  # Always apply
    ... )
    >>> result = dilation_transform(image=image, mask=mask)
    >>> dilated_image = result['image']    # Text is thicker, gaps are filled
    >>> dilated_mask = result['mask']      # Mask is expanded around text regions
    >>>
    >>> # Example 2: Apply erosion to thin text or remove noise
    >>> erosion_transform = A.Morphological(
    ...     scale=(2, 3),          # Random kernel size between 2 and 3
    ...     operation="erosion",   # Shrink white regions (or expand black if inverted)
    ...     p=1.0                  # Always apply
    ... )
    >>> result = erosion_transform(image=image, mask=mask)
    >>> eroded_image = result['image']     # Text is thinner, small noise may be removed
    >>> eroded_mask = result['mask']       # Mask is contracted around text regions
    >>>
    >>> # Note: For document processing, dilation often helps enhance readability for OCR
    >>> # while erosion can help remove noise or separate connected components

c                  *    \ rS rSr% S\S'   S\S'   Srg)Morphological.InitSchemaiZ  r   r.   Literal['erosion', 'dilation']	operationr:   Nr;   r:   rB   rC   rD   r  Z  s    ""11rB   rD   c                N   > [         TU ]  US9  [        SU5      U l        X l        g )Nr   r  )rI   rJ   r   r.   r  )rK   r.   r  rL   rM   s       rC   rJ   Morphological.__init__^  s)     	1+U3
"rB   c                D    [         R                  " XU R                  5      $ )a!  Apply the Morphological transform to the input image.

Args:
    img (np.ndarray): The input image to apply the Morphological transform to.
    kernel (tuple[int, int]): The structuring element (kernel) used for the operation.
    **params (Any): Additional parameters for the transform.

)rP   
morphologyr  )rK   rR   kernelrV   s       rC   rW   Morphological.applyh  s     $$S$..AArB   c                ~    US   n[        X5      n[        R                  " UUU R                  U5      n[	        Xd5      $ )a6  Apply the Morphological transform to the input bounding boxes.

Args:
    bboxes (np.ndarray): The input bounding boxes to apply the Morphological transform to.
    kernel (tuple[int, int]): The structuring element (kernel) used for the operation.
    **params (Any): Additional parameters for the transform.

r{   )r   rP   bboxes_morphologyr  r   )rK   r}   r  rV   r   denormalized_boxesr+  s          rC   r   Morphological.apply_to_bboxesx  sE     Wo/D--NN	
  44rB   c                    U$ )zApply the Morphological transform to the input keypoints.

Args:
    keypoints (np.ndarray): The input keypoints to apply the Morphological transform to.
    **params (Any): Additional parameters for the transform.

r:   )rK   r   rV   s      rC   r    Morphological.apply_to_keypoints  s
     rB   c                d    S[         R                  " [         R                  U R                  5      0$ )zwGenerate parameters for the Morphological transform.

Returns:
    dict[str, float]: The parameters of the transform.

r  )r   getStructuringElementMORPH_ELLIPSEr.   r  s    rC   
get_paramsMorphological.get_params  s*     c//0A0A4::N
 	
rB   )r  r.   ))r'   rI  dilationrF   )r.   ztuple[int, int] | intr  r  rL   r   )rR   r   r  r  rV   r   r   r   )r}   r   r  r  rV   r   r   r   )r   r   rV   r   r   r   r  )r<   r=   r>   r?   r   r   r   r   rD   rJ   rW   r   r   r  rA   r   r   s   @rC   r#   r#     s    FP H2, 2 (.4>	#$# 2# 	# #BB  B 	B
 
B 55  5 	5
 
56  
		
 	
rB   r#   )8r   
__future__r   randomtypingr   r   r   r   warningsr   r   numpyrA  albucorer	   r
   r   pydanticr   r   r   r   r   typing_extensionsr   "albumentations.augmentations.utilsr   albumentations.core.bbox_utilsr   r   r   albumentations.core.pydanticr   r   r   r   (albumentations.core.transforms_interfacer   r   $albumentations.core.type_definitionsr   albumentations.core.utilsr    r    rP   __all__NUM_PADS_XYNUM_PADS_ALL_SIDESr$   r!   r&   r"   r%   r#   r:   rB   rC   <module>r     s    #  0 0  
  F F  # : 
  = . &  }
- }
@t	
] t	
nc
v c
L{
 {
|t= t=n[
M [
rB   