U
    h~8                     @  s   d dl mZ d dlZd dlmZmZmZ d dlZd dl	m
Z
mZ ddlmZmZ ddd	d
dddgZddddddhZdddddZG dd deZG dd deZ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d(dd"ddddd%d&d	ZdS ))    )annotationsN)AnyLiteralSequence)'NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONSPAIR   )DataProcessorParamsangle_to_2pi_rangecheck_keypoints%convert_keypoints_from_albumentations#convert_keypoints_to_albumentationsfilter_keypointsKeypointsProcessorKeypointParamsxyyxxyaxysxyasxysa
np.ndarray)anglesreturnc                 C  s   t | dt j S )N   )npmodpi)r    r   G/tmp/pip-unpacked-wheel-e8onvpoz/albumentations/core/keypoints_utils.pyr      s    c                      st   e Zd ZdZddddddd fdd	Zd
d fddZeddddZeddddZddddZ	  Z
S )r   a  Parameters of keypoints

    Args:
        format (str): format of keypoints. Should be 'xy', 'yx', 'xya', 'xys', 'xyas', 'xysa'.

            x - X coordinate,

            y - Y coordinate

            s - Keypoint scale

            a - Keypoint orientation in radians or degrees (depending on KeypointParams.angle_in_degrees)
        label_fields (list): list of fields that are joined with keypoints, e.g labels.
            Should be same type as keypoints.
        remove_invisible (bool): to remove invisible points after transform or not
        angle_in_degrees (bool): angle in degrees or radians in 'xya', 'xyas', 'xysa' keypoints
        check_each_transform (bool): if `True`, then keypoints will be checked after each dual transform.
            Default: `True`

    NTstrzSequence[str] | Noneboolformatlabel_fieldsremove_invisibleangle_in_degreescheck_each_transformc                   s$   t  || || _|| _|| _d S N)super__init__r&   r'   r(   )selfr$   r%   r&   r'   r(   	__class__r   r    r+   3   s    zKeypointParams.__init__dict[str, Any]r   c                   s&   t   }|| j| j| jd |S )N)r&   r'   r(   )r*   to_dict_privateupdater&   r'   r(   )r,   datar-   r   r    r1   @   s    
zKeypointParams.to_dict_privatec                 C  s   dS )NTr   clsr   r   r    is_serializableK   s    zKeypointParams.is_serializablec                 C  s   dS )Nr   r   r4   r   r   r    get_class_fullnameO   s    z!KeypointParams.get_class_fullnamec                 C  s.   d| j  d| j d| j d| j d| j dS )NzKeypointParams(format=z, label_fields=z, remove_invisible=z, angle_in_degrees=z, check_each_transform=)r#   r,   r   r   r    __repr__S   s    ,zKeypointParams.__repr__)NTTT)__name__
__module____qualname____doc__r+   r1   classmethodr6   r7   r:   __classcell__r   r   r-   r    r      s       c                      s   e Zd Zdddd fddZeddd	d
ZdddddZddddddZddddddZddddddZ	ddddddZ
  ZS )r   Nr   zdict[str, str] | None)paramsadditional_targetsc                   s   t  || d S r)   )r*   r+   )r,   rA   rB   r-   r   r    r+   \   s    zKeypointsProcessor.__init__r!   r0   c                 C  s   dS )N	keypointsr   r9   r   r   r    default_data_name_   s    z$KeypointsProcessor.default_data_namer/   None)r3   r   c                   s2   | j jr.t fdd| j jD s.d}t|d S )Nc                 3  s   | ]}| kV  qd S r)   r   ).0ir3   r   r    	<genexpr>d   s     z7KeypointsProcessor.ensure_data_valid.<locals>.<genexpr>zaYour 'label_fields' are not valid - them must have same names as params in 'keypoint_params' dict)rA   r%   all
ValueError)r,   r3   msgr   rH   r    ensure_data_validc   s    "z$KeypointsProcessor.ensure_data_validr   tuple[int, int])r3   image_shaper   c                 C  s   |  t ||| jjdS )N)r&   )r   rA   r&   r,   r3   rO   r   r   r    filterh   s    zKeypointsProcessor.filterc                 C  s   t || d S r)   )r   rP   r   r   r    checkl   s    zKeypointsProcessor.checkc                 C  s(   |j s
|S | j}t||j||j|jdS N)check_validityr'   )sizerA   r   r$   r&   r'   r,   r3   rO   rA   r   r   r    convert_from_albumentationso   s    z.KeypointsProcessor.convert_from_albumentationsc                 C  s(   |j s
|S | j}t||j||j|jdS rS   )rU   rA   r   r$   r&   r'   rV   r   r   r    convert_to_albumentations   s    z,KeypointsProcessor.convert_to_albumentations)N)r;   r<   r=   r+   propertyrD   rM   rQ   rR   rW   rX   r@   r   r   r-   r    r   [   s   rN   rE   )rC   rO   r   c                   sD  |dd \dddf dddf  t dk kB s^t dk kB rt dk kB d t dk kB d }g }fddttt|B D }td|jd tkr@dddf  t  dk  dt	j
 kB d }t|dkr@ fdd|D }td|dS )	u  Check if keypoint coordinates are within valid ranges for the given image shape.

    This function validates that:
    1. All x-coordinates are within [0, width)
    2. All y-coordinates are within [0, height)
    3. If angles are present (i.e., keypoints have more than 2 columns),
       they are within the range [0, 2π)

    Args:
        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.
                                Each row represents a keypoint with at least (x, y) coordinates.
                                If present, the third column is assumed to be the angle.
        image_shape (Tuple[int, int]): The shape of the image (height, width).

    Raises:
        ValueError: If any keypoint coordinate is outside the valid range, or if any angle is invalid.
                    The error message will detail which keypoints are invalid and why.

    Note:
        - The function assumes that keypoint coordinates are in absolute pixel values, not normalized.
        - Angles, if present, are assumed to be in radians.
        - The constant PAIR should be defined elsewhere in the module, typically as 2.
    Nr   r   r   c                   sZ   g | ]R}d |krdnd d|  d|kr2n  d|krH| n|  d	qS )z	Expected xy for keypoint z to be in the range [0.0, z], got .r   rF   idx)height	invalid_xrC   widthrZ   r[   r   r    
<listcomp>   s   z#check_keypoints.<locals>.<listcomp>
c                   s$   g | ]}d  |  d|  qS )z2Keypoint angle must be in range [0, 2 * PI). Got: r\   r   r^   )r   rC   r   r    rc      s   )r   anywheresortedsetrK   joinshaper   mathr   len)rC   rO   Z	invalid_yZerror_messagesZinvalid_anglesr   )r   r`   ra   rC   rb   rZ   r[   r    r      s$    ", r"   )rC   rO   r&   r   c                 C  sl   |s| S | j s| S |dd \}}| dddf | dddf  }}|dk||k @ |dk@ ||k @ }| | S )a  Filter keypoints to remove those outside the image boundaries.

    Args:
        keypoints: A numpy array of shape (N, 2+) where N is the number of keypoints.
                   Each row represents a keypoint (x, y, ...).
        image_shape: A tuple (height, width) representing the image dimensions.
        remove_invisible: If True, remove keypoints outside the image boundaries.

    Returns:
        A numpy array of filtered keypoints.
    Nr   r   r   )rU   )rC   rO   r&   r`   rb   rZ   r[   Zvisibler   r   r    r      s    " FTz3Literal[('xy', 'yx', 'xya', 'xys', 'xyas', 'xysa')])rC   source_formatrO   rT   r'   r   c           
   	   C  sR  |t krtd| dt  ddddgddddgddddgddddgddddgddddgd}|| }tj| jd tftjd	}t|D ],\}}	|	dk	r| dd|	f |dd|f< q|r|d dk	rt|dddf |dddf< t	|dddf |dddf< | jd t
|kr>t|| ddt
|df f}|rNt|| |S )
u  Convert keypoints from various formats to the Albumentations format.

    This function takes keypoints in different formats and converts them to the standard
    Albumentations format: [x, y, angle, scale]. If the input format doesn't include
    angle or scale, these values are set to 0.

    Args:
        keypoints (np.ndarray): Array of keypoints with shape (N, 2+), where N is the number of keypoints.
                                The number of columns depends on the source_format.
        source_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]): The format of the input keypoints.
            - "xy": [x, y]
            - "yx": [y, x]
            - "xya": [x, y, angle]
            - "xys": [x, y, scale]
            - "xyas": [x, y, angle, scale]
            - "xysa": [x, y, scale, angle]
        image_shape (tuple[int, int]): The shape of the image (height, width).
        check_validity (bool, optional): If True, check if the converted keypoints are within the image boundaries.
                                         Defaults to False.
        angle_in_degrees (bool, optional): If True, convert input angles from degrees to radians.
                                           Defaults to True.

    Returns:
        np.ndarray: Array of keypoints in Albumentations format [x, y, angle, scale] with shape (N, 4+).
                    Any additional columns from the input keypoints are preserved and appended after the
                    first 4 columns.

    Raises:
        ValueError: If the source_format is not one of the supported formats.

    Note:
        - Angles are converted to the range [0, 2π) radians.
        - If the input keypoints have additional columns beyond what's specified in the source_format,
          these columns are preserved in the output.
    zUnknown source_format . Supported formats are: r   r   Nr      r   r   r   r   r   r   )Zdtype)keypoint_formatsrK   r   zerosrj   r   Zfloat32	enumerateradiansr   rl   column_stackr   )
rC   rm   rO   rT   r'   Zformat_to_indicesindicesZprocessed_keypointsrG   r_   r   r   r    r      s,    *





	" "
)rC   target_formatrO   rT   r'   r   c              	   C  s  |t krtd| dt  | dddf | dddf | dddf | dddf f\}}}}t|}|rtt||||f| |rt|}||g||g|||g|||g||||g||||gd}	t|	| }
| jd tkrt|
| ddtdf fS |
S )	u  Convert keypoints from Albumentations format to various other formats.

    This function takes keypoints in the standard Albumentations format [x, y, angle, scale]
    and converts them to the specified target format.

    Args:
        keypoints (np.ndarray): Array of keypoints in Albumentations format with shape (N, 4+),
                                where N is the number of keypoints. Each row represents a keypoint
                                [x, y, angle, scale, ...].
        target_format (Literal["xy", "yx", "xya", "xys", "xyas", "xysa"]): The desired output format.
            - "xy": [x, y]
            - "yx": [y, x]
            - "xya": [x, y, angle]
            - "xys": [x, y, scale]
            - "xyas": [x, y, angle, scale]
            - "xysa": [x, y, scale, angle]
        image_shape (tuple[int, int]): The shape of the image (height, width).
        check_validity (bool, optional): If True, check if the keypoints are within the image boundaries.
                                         Defaults to False.
        angle_in_degrees (bool, optional): If True, convert output angles to degrees.
                                           If False, angles remain in radians.
                                           Defaults to True.

    Returns:
        np.ndarray: Array of keypoints in the specified target format with shape (N, 2+).
                    Any additional columns from the input keypoints beyond the first 4
                    are preserved and appended after the converted columns.

    Raises:
        ValueError: If the target_format is not one of the supported formats.

    Note:
        - Input angles are assumed to be in the range [0, 2π) radians.
        - If the input keypoints have additional columns beyond the first 4,
          these columns are preserved in the output.
        - The constant NUM_KEYPOINTS_COLUMNS_IN_ALBUMENTATIONS should be defined
          elsewhere in the module, typically as 4.
    zUnknown target_format rn   Nr   r   r   ro   rp   )	rq   rK   r   r   r   ru   degreesrj   r   )rC   rw   rO   rT   r'   rZ   r[   ZangleZscaleZformat_to_columnsresultr   r   r    r   4  s&    -D


	)FT)FT)
__future__r   rk   typingr   r   r   Znumpyr   Zalbumentations.core.typesr   r   utilsr	   r
   __all__rq   r   r   r   r   r   r   r   r   r   r   r    <module>   s2   
>67$  P  