U
    hB                     @  s  d dl mZ d dlZd dlZd dlmZmZmZ d dlZd dl	Z
d dlmZ d dlmZ d dlmZ d dlmZ d dlmZmZmZ d d	lmZmZ d d
lmZmZmZ ddlmZ  dddgZ!dZ"G dd deZ#G dd deZ$G dd deZ%G dd deZ&dS )    )annotationsN)AnyTuplecast)ProjectiveTransform)Literal)
functional)Affine)BorderModeTypeInterpolationTypeSymmetricRangeType)BaseTransformInitSchemaDualTransform)	ColorTypeScaleFloatTypeTargets   RotateRandomRotate90
SafeRotateg|=c                   @  s|   e Zd ZdZejejejejfZ	dddddddZ
dd	d
dZdddddddZdddddddZdd	ddZdS )r   zRandomly rotate the input by 90 degrees zero or more times.

    Args:
        p: probability of applying the transform. Default: 0.5.

    Targets:
        image, mask, bboxes, keypoints

    Image types:
        uint8, float32

    
np.ndarrayintr   )imgfactorparamsreturnc                 K  s   t ||S N)
fgeometricZrot90)selfr   r   r    r   Q/tmp/pip-unpacked-wheel-e8onvpoz/albumentations/augmentations/geometric/rotate.pyapply-   s    zRandomRotate90.applydict[str, int]r   c                 C  s   dt ddiS )Nr   r      )randomrandintr   r   r   r    
get_params0   s    zRandomRotate90.get_params)bboxesr   r   r   c                 K  s   t ||S r   )r   Zbboxes_rot90)r   r)   r   r   r   r   r    apply_to_bboxes4   s    zRandomRotate90.apply_to_bboxes)	keypointsr   r   r   c                 K  s   t |||d S )Nshape)r   Zkeypoints_rot90)r   r+   r   r   r   r   r    apply_to_keypoints7   s    z!RandomRotate90.apply_to_keypointsz	tuple[()]c                 C  s   dS )Nr   r   r'   r   r   r    get_transform_init_args_names:   s    z,RandomRotate90.get_transform_init_args_namesN)__name__
__module____qualname____doc__r   IMAGEMASKBBOXES	KEYPOINTS_targetsr!   r(   r*   r-   r.   r   r   r   r    r      s   c                   @  s6   e Zd ZU ded< ded< ded< ded< ded	< d
S )RotateInitSchemar   limitr   interpolationr
   border_modeColorType | Nonevalue
mask_valueNr/   r0   r1   __annotations__r   r   r   r    r8   >   s
   
r8   c                      s  e Zd ZdZejejejejfZ	G dd de
Zdejejddddddf	d	d
d
ddddddd	 fddZddd
d
d
d
dddddZddd
d
d
d
dddddZddd
d
d
d
dddddZddd
d
d
d
ddddd Zed
d
dd!d"d#d$Zd%d%d%d&d'd(Zd)d*d+d,Z  ZS )-r   u  Rotate the input by an angle selected randomly from the uniform distribution.

    Args:
        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
        value (int, float, list of ints, list of float): Padding value if border_mode is cv2.BORDER_CONSTANT.
        mask_value (int, float, list of ints, list of float): 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
        p (float): Probability of applying the transform. Default: 0.5.

    Targets:
        image, mask, bboxes, keypoints

    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 θ is randomly sampled from the range specified by 'limit'.
        2. The image is rotated around its center by θ degrees.
        3. The rotation matrix R is:
           R = [cos(θ)  -sin(θ)]
               [sin(θ)   cos(θ)]
        4. Each point (x, y) in the image is transformed to (x', y') by:
           [x']   [cos(θ)  -sin(θ)] [x - cx]   [cx]
           [y'] = [sin(θ)   cos(θ)] [y - cy] + [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.

    Example:
        >>> import numpy as np
        >>> import albumentations as A
        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
        >>> transform = A.Rotate(limit=45, p=1.0)
        >>> result = transform(image=image)
        >>> rotated_image = result['image']
        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees
    c                   @  s   e Zd ZU ded< ded< dS )zRotate.InitSchema#Literal[('largest_box', 'ellipse')]rotate_methodboolcrop_borderNr?   r   r   r   r    
InitSchema   s   
rE   iZ   Nlargest_boxF      ?r   r   r<   rA   rC   bool | Nonefloat)	r9   r:   r;   r=   r>   rB   rD   always_applypc
           
        sL   t  j|	|d ttttf || _|| _|| _|| _|| _	|| _
|| _d S )N)rM   rL   )super__init__r   r   rK   r9   r:   r;   r=   r>   rB   rD   )
r   r9   r:   r;   r=   r>   rB   rD   rL   rM   	__class__r   r    rO      s    zRotate.__init__r   r   )r   anglex_minx_maxy_miny_maxr   r   c           	      K  s4   t ||| j| j| j}| jr0t|||||S |S r   )r   rotater:   r;   r=   rD   fcropscrop)	r   r   rR   rS   rT   rU   rV   r   img_outr   r   r    r!      s    
zRotate.apply)maskrR   rS   rT   rU   rV   r   r   c           	      K  s4   t ||tj| j| j}| jr0t|||||S |S r   )	r   rW   cv2ZINTER_NEARESTr;   r>   rD   rX   rY   )	r   r[   rR   rS   rT   rU   rV   r   rZ   r   r   r    apply_to_mask   s    
zRotate.apply_to_mask)r)   rR   rS   rT   rU   rV   r   r   c           
      K  sB   |d d d }t ||| j|}	| jr>t|	||||f|S |	S Nr,      )r   Zbboxes_rotaterB   rD   rX   Zcrop_bboxes_by_coords)
r   r)   rR   rS   rT   rU   rV   r   image_shapeZ
bboxes_outr   r   r    r*      s
    
zRotate.apply_to_bboxes)r+   rR   rS   rT   rU   rV   r   r   c           	      K  s8   t |||d d d }| jr4t|||||fS |S r^   )r   Zkeypoints_rotaterD   rX   Zcrop_keypoints_by_coords)	r   r+   rR   rS   rT   rU   rV   r   Zkeypoints_outr   r   r    r-      s    
zRotate.apply_to_keypointsr"   )heightwidthrR   r   c              	   C  s>  t |}|| k}|r|| fn| |f\}}tt |tt | }}|d| | | kslt|| tk rd| }|r|| || fn|| || f\}	}
n:|| ||  }|| | |  | | | ||  |  }	}
tdt|d |	d  t|t|d |	d  tdt| d |
d  t| t| d |
d  dS )aT  Given a rectangle of size wxh that has been rotated by 'angle' (in
        degrees), computes the width and height of the largest possible
        axis-aligned rectangle (maximal area) within the rotated rectangle.

        Reference:
            https://stackoverflow.com/questions/16702966/rotate-image-and-crop-out-black-borders
        g       @rI   r   r_   rS   rT   rU   rV   )	mathradiansabssincosSMALL_NUMBERmaxr   min)ra   rb   rR   Zwidth_is_longerZ	side_longZ
side_shortZsin_aZcos_axwrhrZcos_2ar   r   r    _rotated_rect_with_max_area   s    	
$**z"Rotate._rotated_rect_with_max_areadict[str, Any]r   datar   c                 C  s\   dt j| j i}| jrD|d d d \}}|| |||d  n|ddddd |S )NrR   r,   r_   rc   )r%   uniformr9   rD   updatero   )r   r   rr   Z
out_paramsra   rb   r   r   r    get_params_dependent_on_data   s    z#Rotate.get_params_dependent_on_datatuple[str, ...]r#   c                 C  s   dS )N)r9   r:   r;   r=   r>   rB   rD   r   r'   r   r   r    r.     s    z$Rotate.get_transform_init_args_names)r/   r0   r1   r2   r   r3   r4   r5   r6   r7   r8   rE   r\   INTER_LINEARBORDER_REFLECT_101rO   r!   r]   r*   r-   staticmethodro   rv   r.   __classcell__r   r   rP   r    r   H   s*   9$ 
c                
      s   e Zd ZdZejejejejfZ	G dd de
Zdejejdddddfdd	d	d
d
dddd fddZddddZdddddddZddddddZ  ZS )r   ut  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.

    Args:
        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
        value (int, float, list of int, list of float): Padding value if border_mode is cv2.BORDER_CONSTANT.
        mask_value (int, float, list of int, list of float): 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'
        p (float): Probability of applying the transform. Default: 0.5.

    Targets:
        image, mask, bboxes, keypoints

    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 θ is randomly sampled from the range specified by 'limit'.
        2. The image is rotated around its center by θ degrees.
        3. The rotation matrix R is:
           R = [cos(θ)  -sin(θ)]
               [sin(θ)   cos(θ)]
        4. The scaling factor s is calculated to ensure the rotated image fits within the original frame:
           s = min(width / (width * |cos(θ)| + height * |sin(θ)|),
                   height / (width * |sin(θ)| + height * |cos(θ)|))
        5. The combined transformation matrix T is:
           T = [s*cos(θ)  -s*sin(θ)  tx]
               [s*sin(θ)   s*cos(θ)  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']   [s*cos(θ)  -s*sin(θ)] [x - cx]   [cx]
           [y'] = [s*sin(θ)   s*cos(θ)] [y - cy] + [cy]
           where (cx, cy) is the center of the image.

    Example:
        >>> import numpy as np
        >>> import albumentations as A
        >>> image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
        >>> transform = A.SafeRotate(limit=45, p=1.0)
        >>> result = transform(image=image)
        >>> rotated_image = result['image']
        # rotated_image will be the input image rotated by a random angle between -45 and 45 degrees,
        # scaled to fit within the original 100x100 frame
    c                   @  s   e Zd ZU ded< dS )zSafeRotate.InitSchemarA   rB   Nr?   r   r   r   r    rE   K  s   
rE   rF   NrH   rI   r   r   r<   rA   rJ   rK   )r9   r:   r;   r=   r>   rB   rL   rM   c	           	        st   |d krdn|}|d krdn|}t  j||||||d||d	 ttttf || _|| _|| _|| _|| _	|| _
d S )Nr   T)	rW   r:   modecvalZ	cval_maskrB   Z
fit_outputrM   rL   )rN   rO   r   r   rK   r9   r:   r;   r=   r>   rB   )	r   r9   r:   r;   r=   r>   rB   rL   rM   rP   r   r    rO   N  s&    zSafeRotate.__init__rw   r#   c                 C  s   dS )N)r9   r:   r;   r=   r>   rB   r   r'   r   r   r    r.   m  s    z(SafeRotate.get_transform_init_args_namesztuple[float, float]ztuple[int, int]z,tuple[ProjectiveTransform, dict[str, float]])rR   centerr`   r   c                 C  s   |d d \}}t ||d}t|d }t|d }t|| ||  }	t|| ||  }
|d  |	d |d  7  < |d  |
d |d  7  < ||	 }||
 }t|ddgd|dgdddgg}|t|dddgg }t|d	||d
fS )Nr_   g      ?)r   r   )r   r   )r   r_   r   )r   r_   r   )matrix)rl   y)r\   ZgetRotationMatrix2Drf   r   nparrayZvstackr   )r   rR   r~   r`   ra   rb   Zrotation_matZabs_cosZabs_sinZnew_wZnew_hZscale_xZscale_yZ	scale_matr   r   r   r    _create_safe_rotate_matrixp  s    "z%SafeRotate._create_safe_rotate_matrixrp   rq   c                 C  sp   |d d d }t | jd | jd }t|}t|}| |||\}}| |||\}	}
||||	|dS )Nr,   r_   r   r   )rW   scaler   bbox_matrixZoutput_shape)r%   rt   r9   r   r~   Zcenter_bboxr   )r   r   rr   r`   rR   Zimage_centerZbbox_centerr   r   r   _r   r   r    rv     s    

z'SafeRotate.get_params_dependent_on_data)r/   r0   r1   r2   r   r3   r4   r5   r6   r7   r8   rE   r\   rx   ry   rO   r.   r   rv   r{   r   r   rP   r    r   	  s   ?")'
__future__r   rd   r%   typingr   r   r   r\   Znumpyr   Zskimage.transformr   Ztyping_extensionsr   Z"albumentations.augmentations.cropsr   rX   Z1albumentations.augmentations.geometric.transformsr	   Zalbumentations.core.pydanticr
   r   r   Z(albumentations.core.transforms_interfacer   r   Zalbumentations.core.typesr   r   r    r   __all__ri   r   r8   r   r   r   r   r   r    <module>   s(   
!
 B