U
    hA                     @  sR  d dl mZ d dlZd dlmZmZm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 d	d
dddddgZed/ddddddd
ZdddddddZd0dddddddd	ZeddddddZdddd d dd!d"dZdd#d#dd$d%dZddd d&dddd'd(dZedddd d dd)d*d+Zedddd,d-d.ZdS )1    )annotationsN)MAX_VALUES_BY_DTYPEis_grayscale_imagepreserve_channel_dim)Literal)random_utils)split_uniform_grid)handle_empty_array)MONO_CHANNEL_DIMENSIONS	ColorTypecutoutchannel_dropoutfilter_keypoints_in_holesgenerate_random_fillfilter_bboxes_by_holescalculate_grid_dimensionsgenerate_grid_holesz
np.ndarrayz"int | tuple[int, ...] | np.ndarrayr   )imgchannels_to_drop
fill_valuereturnc                 C  s,   t | rd}t||  } || d|f< | S )Nz0Only one channel. ChannelDropout is not defined..)r   NotImplementedErrorcopy)r   r   r   msg r   S/tmp/pip-unpacked-wheel-e8onvpoz/albumentations/augmentations/dropout/functional.pyr      s    znp.dtypeztuple[int, ...]znp.random.RandomState | None)dtypeshaperandom_stater   c                 C  sf   t |  }t| tjr.tjd|d || |dS t| tjrTtjd|||d| S t	d|  dS )a9  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.

    Args:
        dtype (np.dtype): The data type of the array to be generated.
        shape (tuple[int, ...]): The shape of the array to be generated.
        random_state (np.random.RandomState | None): The random state to use for generating values.
            If None, the default numpy random state is used.

    Returns:
        np.ndarray: A numpy array of the specified shape and dtype, filled with random values.

    Raises:
        ValueError: If the input dtype is neither integer nor floating-point.

    Examples:
        >>> import numpy as np
        >>> random_state = np.random.RandomState(42)
        >>> result = generate_random_fill(np.dtype('uint8'), (2, 2), random_state)
        >>> print(result)
        [[172 251]
         [ 80 141]]
    r      )sizer   r   )r    r   zUnsupported dtype: N)
r   npZ
issubdtypeintegerr   randintZfloatinguniformastype
ValueError)r   r   r   Z	max_valuer   r   r   r   &   s    zColorType | Literal['random'])r   holesr   r   r   c           
      C  s   |   } t|ttttfr*tj|| jd}|D ]\}}}}t|t	r|dkr| j
tkrf|| || fn|| || | jd f}t| j||}	|	| ||||f< q.|| ||||f< q.| S )a
  Apply cutout augmentation to the image by cutting out holes and filling them
    with either a given value or random noise.

    Args:
        img (np.ndarray): The image to augment. Can be a 2D (grayscale) or 3D (color) array.
        holes (np.ndarray): An array of holes with shape (num_holes, 4).
            Each hole is represented as [x1, y1, x2, y2].
        fill_value (Union[ColorType, Literal["random"]], optional): The fill value to use for the holes.
            Can be a single integer, a tuple or list of numbers for multichannel images,
            or the string "random" to fill with random noise.
        random_state (np.random.RandomState | None, optional): The random state to use for generating
            random fill values. If None, a new random state will be used. Defaults to None.

    Returns:
        np.ndarray: The augmented image with cutout holes applied.

    Raises:
        ValueError: If the fill_value is not of the expected type.

    Note:
        - The function creates a copy of the input image before applying the cutout.
        - For multichannel images, the fill_value should match the number of channels.
        - When using "random" fill, the random values are generated to match the image's dtype and shape.

    Example:
        >>> import numpy as np
        >>> img = np.ones((100, 100, 3), dtype=np.uint8) * 255
        >>> holes = np.array([[20, 20, 40, 40], [60, 60, 80, 80]])
        >>> result = cutout(img, holes, fill_value=0)
        >>> print(result.shape)
        (100, 100, 3)
    r   random   )r   
isinstanceintfloattuplelistr!   arrayr   strndimr
   r   r   )
r   r'   r   r   x_miny_minx_maxy_maxr   Zrandom_fillr   r   r   r   M   s    &)	keypointsr'   r   c           
      C  s   | dddf ddt jf }| dddf ddt jf }|dddf }|dddf }|dddf }|dddf }||k||k @ ||k@ ||k @ }t j|dd }	| |	 S )a  Filter out keypoints that are inside any of the holes.

    Args:
        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:
        np.ndarray: Array of keypoints that are not inside any hole.
    Nr   r   r*      Zaxis)r!   Znewaxisany)
r7   r'   Zkp_xZkp_yZhole_x1Zhole_y1Zhole_x2Zhole_y2Zinside_holeZvalid_keypointsr   r   r   r      s     ztuple[int, int]r-   )bboxesr'   image_shapemin_areamin_visibilityr   c                 C  sB  t | dkst |dkr| S tj|tjd}|D ]*}|t\}}}	}
d|||
||	f< q0| t}|dddf |dddf |dddf |dddf f\}}}	}
|	| |
|  }tjt | td}tt | D ]`}t||| |
| || |	| f }|| | }d|||   }||ko2||k||< q| | S )a  Filter bounding boxes based on their remaining visible area and visibility ratio after intersection with holes.

    Args:
        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:
        np.ndarray: Filtered array of bounding boxes.
    r   r(   r   Nr*   r8   )	lenr!   zerosZuint8r%   r,   boolrangesum)r;   r'   r<   r=   r>   Z	hole_maskZholer3   r4   r5   r6   Z
bboxes_int	box_areasmaskiZintersection_areaZremaining_areavisibility_ratior   r   r   r      s     
D*ztuple[int, int] | None)r<   unit_size_rangeholes_number_xyr   c           
      C  s   | dd \}}|dk	rJ|d t | dd kr8tdtj| }||fS |rn|\}}|| }|| }	|	|fS td|d }td|d }	|	|fS )a]  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

    Args:
        image_shape (tuple[int, int]): The shape of the image as (height, width).
        unit_size_range (tuple[int, int] | None, optional): 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, optional): The number of holes in the x and y directions.
            If provided, the grid dimensions will be calculated to fit this number of holes.

    Returns:
        tuple[int, int]: The calculated grid unit dimensions as (unit_height, unit_width).

    Raises:
        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:
        >>> image_shape = (100, 200)
        >>> calculate_grid_dimensions(image_shape, unit_size_range=(10, 20))
        (15, 15)  # Random value between 10 and 20

        >>> calculate_grid_dimensions(image_shape, holes_number_xy=(5, 10))
        (20, 20)  # 100 // 5 and 200 // 10

        >>> calculate_grid_dimensions(image_shape)
        (10, 20)  # Default calculation: max(2, dimension // 10)
    Nr*   r   z8Grid size limits must be within the shortest image edge.
   )minr&   r   r#   max)
r<   rH   rI   heightwidthZ	unit_sizeZholes_number_xZholes_number_yZ
unit_widthZunit_heightr   r   r   r      s    +
rA   )r<   gridratiorandom_offsetr   shift_xyr   c                 C  sh  | dd \}}t | ||}|dddf |dddf  }	|dddf |dddf  }
t|	| d|	d t}t|
| d|
d t}|	| }|
| }|r|dk	r|d|d }|d|d }n t||d }t||d }t|dddf | d|| }t|dddf | d|| }t|| |}t|| |}t||||fS )a	  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.

    Args:
        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.
        random_state (np.random.RandomState | None): The random state for generating random offsets
            and shuffling. If None, a new RandomState will be created.
        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.

    Returns:
        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:
        >>> image_shape = (100, 100)
        >>> grid = (5, 5)
        >>> ratio = 0.5
        >>> random_offset = True
        >>> random_state = np.random.RandomState(42)
        >>> shift_xy = (0, 0)
        >>> holes = generate_grid_holes(image_shape, grid, ratio, random_offset, random_state, shift_xy)
        >>> print(holes.shape)
        (25, 4)
        >>> print(holes[0])  # Example output: [x1, y1, x2, y2] of the first hole
        [ 1 21 11 31]
    Nr*   r   r8   r   )	r   r!   Zclipr%   r,   r#   Z	full_likeZminimumZcolumn_stack)r<   rO   rP   rQ   r   rR   rM   rN   cellsZcell_heightsZcell_widthsZhole_heightsZhole_widthsZmax_offset_yZmax_offset_xZoffset_yZoffset_xr3   r4   r5   r6   r   r   r   r     s$    3  "")r;   dropout_maskr<   r=   r>   r   c                 C  s*  |\}}t jd|d|f \}}|dddf | dddddf k|dddf | dddddf k@ |dddf | dddddf k@ |dddf | dddddf k@ }	| dddf | dddf  | dddf | dddf   }
t j|	|  @ dd}||
 }||k||k@ }| | S )a  Filter out bounding boxes based on their intersection with the dropout mask.

    Args:
        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:
        np.ndarray: Filtered array of bounding boxes.
    Nr   r*   r   r8   )r   r*   r9   )r!   ZogridrC   Zsqueeze)r;   rT   r<   r=   r>   rM   rN   yxZ	box_masksrD   Zvisible_areasrG   Z	keep_maskr   r   r   mask_dropout_bboxesk  s    """"@rW   )r7   rT   r   c                   s    t  fdd| D }| | S )Nc                   s*   g | ]"} t |d  t |d f  qS )r   r   )r,   ).0ZkprT   r   r   
<listcomp>  s     z*mask_dropout_keypoints.<locals>.<listcomp>)r!   r0   )r7   rT   Zkeep_indicesr   rY   r   mask_dropout_keypoints  s    r[   )r   )N)
__future__r   Znumpyr!   Zalbucorer   r   r   Ztyping_extensionsr   Zalbumentationsr   Z1albumentations.augmentations.geometric.functionalr   Z"albumentations.augmentations.utilsr	   Zalbumentations.core.typesr
   r   __all__r   r   r   r   r   r   r   rW   r[   r   r   r   r   <module>   s<    + :2?T-