o
    e@                  
   @   s  d dl mZmZmZ d dlZd dlZdZdejdee	e	f dejfddZ
d	ejd
ejdejfddZ	d7dejdedejfddZdejdee	e	f dejfddZdejdejfddZdejdejfddZdejdeej fddZ		d8deej dee d ee deej fd!d"Zdejdejfd#d$Z	%d9dejd&ed'edejfd(d)Zdeej fd*d+Zd,edeejejejeej ejf fd-d.Zdejd/ejdejfd0d1Zdejd2edejfd3d4Zdejdejfd5d6ZdS ):    )ListOptionalTupleN   polygonresolution_whreturnc                 C   s,   |\}}t ||f}tj|| gdd |S )a  Generate a mask from a polygon.

    Args:
        polygon (np.ndarray): The polygon for which the mask should be generated,
            given as a list of vertices.
        resolution_wh (Tuple[int, int]): The width and height of the desired resolution.

    Returns:
        np.ndarray: The generated 2D mask, where the polygon is marked with
            `1`'s and the rest is filled with `0`'s.
       )color)npzeroscv2fillPoly)r   r   widthheightmask r   J/var/www/myenv/lib/python3.10/site-packages/supervision/detection/utils.pypolygon_to_mask	   s   r   
boxes_trueboxes_detectionc                 C   s   dd }|| j }||j }t| dddddf |ddddf }t| dddddf |ddddf }ttj|| dddd}||dddf | |  S )a  
    Compute Intersection over Union (IoU) of two sets of bounding boxes -
        `boxes_true` and `boxes_detection`. Both sets
        of boxes are expected to be in `(x_min, y_min, x_max, y_max)` format.

    Args:
        boxes_true (np.ndarray): 2D `np.ndarray` representing ground-truth boxes.
            `shape = (N, 4)` where `N` is number of true objects.
        boxes_detection (np.ndarray): 2D `np.ndarray` representing detection boxes.
            `shape = (M, 4)` where `M` is number of detected objects.

    Returns:
        np.ndarray: Pairwise IoU of boxes from `boxes_true` and `boxes_detection`.
            `shape = (N, M)` where `N` is number of true objects and
            `M` is number of detected objects.
    c                 S   s    | d | d  | d | d   S )N   r   r   r	   r   )boxr   r   r   box_area.   s    zbox_iou_batch.<locals>.box_areaNr   r   )a_mina_max)Tr   maximumminimumprodclip)r   r   r   	area_truearea_detectiontop_leftbottom_right
area_interr   r   r   box_iou_batch   s   

..r&         ?predictionsiou_thresholdc                 C   s  d|  kr
dksn J d| d| j \}}|dkr&tj| t|f } t| dddf  }| | } | ddddf }| dddf }t||}|t| }tj|t	d}t
t||D ]\}	\}
}||	 snqc|
|k||k@ }|| @ }qc||  S )	a  
    Perform Non-Maximum Suppression (NMS) on object detection predictions.

    Args:
        predictions (np.ndarray): An array of object detection predictions in
            the format of `(x_min, y_min, x_max, y_max, score)`
            or `(x_min, y_min, x_max, y_max, score, class)`.
        iou_threshold (float, optional): The intersection-over-union threshold
            to use for non-maximum suppression.

    Returns:
        np.ndarray: A boolean array indicating which predictions to keep after n
            on-maximum suppression.

    Raises:
        AssertionError: If `iou_threshold` is not within the
            closed range from `0` to `1`.
    r   r	   zBValue of `iou_threshold` must be in the closed range from 0 to 1, z given.   N   dtype)shaper   c_r   flipargsortr&   eyeonesbool	enumeratezip)r(   r)   rowscolumns
sort_indexboxes
categoriesiouskeepindexioucategory	conditionr   r   r   non_max_suppression;   s*   

rB   xyxyc                 C   sn   t | }|\}}|ddddgf d||ddddgf< |ddddgf d||ddddgf< |S )aL  
    Clips bounding boxes coordinates to fit within the frame resolution.

    Args:
        xyxy (np.ndarray): A numpy array of shape `(N, 4)` where each
            row corresponds to a bounding box in
        the format `(x_min, y_min, x_max, y_max)`.
        resolution_wh (Tuple[int, int]): A tuple of the form `(width, height)`
            representing the resolution of the frame.

    Returns:
        np.ndarray: A numpy array of shape `(N, 4)` where each row
            corresponds to a bounding box with coordinates clipped to fit
            within the frame resolution.
    Nr   r   r	   r   )r   copyr    )rC   r   resultr   r   r   r   r   
clip_boxesq   s
   
,,rF   
boxes_xywhc                 C   sd   |   }| d d df | d d df  |d d df< | d d df | d d df  |d d df< |S )Nr   r   r	   r   )rD   )rG   rC   r   r   r   xywh_to_xyxy   s   ,,rH   masksc                 C   s   | j d }tj|dftd}t| D ]9\}}t|\}}t|dkrKt|dkrKt|t|}}t|t|}	}
||	||
g||ddf< q|S )am  
    Converts a 3D `np.array` of 2D bool masks into a 2D `np.array` of bounding boxes.

    Parameters:
        masks (np.ndarray): A 3D `np.array` of shape `(N, W, H)`
            containing 2D bool masks

    Returns:
        np.ndarray: A 2D `np.array` of shape `(N, 4)` containing the bounding boxes
            `(x_min, y_min, x_max, y_max)` for each mask
    r   r+   r,   N)	r.   r   r   intr5   wherelenminmax)rI   nbboxesir   r7   colsx_minx_maxy_miny_maxr   r   r   mask_to_xyxy   s   
rW   r   c                 C   s,   t | tjt jt j\}}dd |D S )a3  
    Converts a binary mask to a list of polygons.

    Parameters:
        mask (np.ndarray): A binary mask represented as a 2D NumPy array of
            shape `(H, W)`, where H and W are the height and width of
            the mask, respectively.

    Returns:
        List[np.ndarray]: A list of polygons, where each polygon is represented by a
            NumPy array of shape `(N, 2)`, containing the `x`, `y` coordinates
            of the points. Polygons with fewer points than `MIN_POLYGON_POINT_COUNT = 3`
            are excluded from the output.
    c                 S   s(   g | ]}|j d  tkrtj|ddqS )r   r	   axis)r.   MIN_POLYGON_POINT_COUNTr   squeeze).0contourr   r   r   
<listcomp>   s
    z$mask_to_polygons.<locals>.<listcomp>)r   findContoursastyper   uint8	RETR_TREECHAIN_APPROX_SIMPLE)r   contours_r   r   r   mask_to_polygons   s   rf   polygonsmin_areamax_areac                    s<   du r
 du r
| S dd | D } fddt | |D S )a  
    Filters a list of polygons based on their area.

    Parameters:
        polygons (List[np.ndarray]): A list of polygons, where each polygon is
            represented by a NumPy array of shape `(N, 2)`,
            containing the `x`, `y` coordinates of the points.
        min_area (Optional[float]): The minimum area threshold.
            Only polygons with an area greater than or equal to this value
            will be included in the output. If set to None,
            no minimum area constraint will be applied.
        max_area (Optional[float]): The maximum area threshold.
            Only polygons with an area less than or equal to this value
            will be included in the output. If set to None,
            no maximum area constraint will be applied.

    Returns:
        List[np.ndarray]: A new list of polygons containing only those with
            areas within the specified thresholds.
    Nc                 S   s   g | ]}t |qS r   )r   contourArea)r\   r   r   r   r   r^      s    z+filter_polygons_by_area.<locals>.<listcomp>c                    s4   g | ]\}}d u s|kr d u s| kr|qS )Nr   )r\   r   areari   rh   r   r   r^      s    )r6   )rg   rh   ri   aresr   rl   r   filter_polygons_by_area   s   rn   c                 C   s6   t j| dd\}}t j| dd\}}t ||||gS )a  
    Converts a polygon represented by a NumPy array into a bounding box.

    Parameters:
        polygon (np.ndarray): A polygon represented by a NumPy array of shape `(N, 2)`,
            containing the `x`, `y` coordinates of the points.

    Returns:
        np.ndarray: A 1D NumPy array containing the bounding box
            `(x_min, y_min, x_max, y_max)` of the input polygon.
    r   rX   )r   rM   rN   array)r   rS   rU   rT   rV   r   r   r   polygon_to_xyxy   s   rp   皙?
percentageepsilon_stepc                 C   s   |dk s|dkrt dttt| d|  d}t| |kr!| S d}| }	 ||7 }tj| |dd}t||kr;|}nnq&tj|ddS )a  
    Approximates a given polygon by reducing a certain percentage of points.

    This function uses the Ramer-Douglas-Peucker algorithm to simplify the input
        polygon by reducing the number of points
        while preserving the general shape.

    Parameters:
        polygon (np.ndarray): A 2D NumPy array of shape `(N, 2)` containing
            the `x`, `y` coordinates of the input polygon's points.
        percentage (float): The percentage of points to be removed from the
            input polygon, in the range `[0, 1)`.
        epsilon_step (float): Approximation accuracy step.
            Epsilon is the maximum distance between the original curve
            and its approximation.

    Returns:
        np.ndarray: A new 2D NumPy array of shape `(M, 2)`,
            where `M <= N * (1 - percentage)`, containing
            the `x`, `y` coordinates of the
            approximated polygon's points.
    r   r	   z'Percentage must be in the range [0, 1).r   T)closedrX   )
ValueErrorrN   rJ   rL   r   approxPolyDPr   r[   )r   rr   rs   target_pointsepsilonapproximated_pointsnew_approximated_pointsr   r   r   approximate_polygon   s   r{   c                 C   sJ  | j sd S | j}t| j jjdd  }d}d}||krDt|d |d  |d |d  }|d |d |  d |d |d |  d f}t|d t|d }}t|d |d  t|d |d  }}g }	| j j  }
t	|
jd D ]&}|
| }|||||f }|j|krt
||d |d f}|	| qwtj|	tdS )Nr	   r   )r   r   r   r,   )rI   
orig_shapetupledatar.   rM   rJ   cpunumpyranger   resizeappendr   asarrayr4   )yolov8_resultsr|   inference_shapegainpadtopleftbottomright	mask_mapsrI   rQ   r   r   r   r   extract_ultralytics_masks'  s2   *
r   roboflow_resultc                 C   s@  | d st dt dt dd d fS g }g }g }g }g }t| d d }t| d d }| d D ]}|d }	|d }
|d }|d }|	|d	  }|
|d	  }|| }|| }d
|vr|||||g ||d  ||d  d|v r~||d  q2t|d
 dkrt jdd |d
 D td}t|||fd}|||||g ||d  ||d  || d|v r||d  q2t|dkrt |nt d}t|dkrt |nt d}t|dkrt |tnt d}t|dkrt j|tdnd }t|dkrt |tnd }|||||fS )Nr(   )r   r+   r   imager   r   xyr   pointsclass_id
confidence
tracker_idr   c                 S   s   g | ]
}|d  |d gqS )r   r   r   )r\   pointr   r   r   r^   m  s    z+process_roboflow_result.<locals>.<listcomp>r,   )r   )	r   emptyrJ   r   rL   ro   r   r`   r4   )r   rC   r   r   rI   tracker_idsimage_widthimage_height
predictionr   r   r   r   rS   rU   rT   rV   r   r   r   r   r   r   process_roboflow_resultL  sV    
  & "r   offsetc                 C   s   | t ||g S )a  
    Parameters:
        xyxy (np.ndarray): An array of shape `(n, 4)` containing the bounding boxes
            coordinates in format `[x1, y1, x2, y2]`
        offset (np.array): An array of shape `(2,)` containing offset values in format
            is `[dx, dy]`.

    Returns:
        np.ndarray: Repositioned bounding boxes.

    Example:
        ```python
        >>> import numpy as np
        >>> import supervision as sv

        >>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
        >>> offset = np.array([5, 5])
        >>> sv.move_boxes(boxes, offset)
        ... array([
        ...     [15, 15, 25, 25],
        ...     [35, 35, 45, 45]
        ... ])
        ```
    )r   hstack)rC   r   r   r   r   
move_boxes  s   r   factorc                 C   sz   | ddddf | ddddf  d }| ddddf | ddddf  | }t j||d  ||d  fddS )aD  
    Scale the dimensions of bounding boxes.

    Parameters:
        xyxy (np.ndarray): An array of shape `(n, 4)` containing the bounding boxes
            coordinates in format `[x1, y1, x2, y2]`
        factor (float): A float value representing the factor by which the box
            dimensions are scaled. A factor greater than 1 enlarges the boxes, while a
            factor less than 1 shrinks them.

    Returns:
        np.ndarray: Scaled bounding boxes.

    Example:
        ```python
        >>> import numpy as np
        >>> import supervision as sv

        >>> boxes = np.array([[10, 10, 20, 20], [30, 30, 40, 40]])
        >>> factor = 1.5
        >>> sv.scale_boxes(boxes, factor)
        ... array([
        ...     [ 7.5,  7.5, 22.5, 22.5],
        ...     [27.5, 27.5, 42.5, 42.5]
        ... ])
        ```
    Nr   r	   rX   )r   concatenate)rC   r   centers	new_sizesr   r   r   scale_boxes  s   ,,"r   c                    s    j \}}} jdd}t||fd \}}d||dk< dtjdtdtjf fd	d
}ddgddgf}|||| }	|||| }
t|	|
ftS )a  
    Calculate the centroids of binary masks in a tensor.

    Parameters:
        masks (np.ndarray): A 3D NumPy array of shape (num_masks, height, width).
            Each 2D array in the tensor represents a binary mask.

    Returns:
        A 2D NumPy array of shape (num_masks, 2), where each row contains the x and y
            coordinates (in that order) of the centroid of the corresponding mask.
    )r	   r   rX   r'   r	   r   indicesrY   r   c                    s   t j | |dS )N)axes)r   	tensordot)r   rY   rI   r   r   sum_over_mask  s   z0calculate_masks_centroids.<locals>.sum_over_maskr   )	r.   sumr   r   ndarrayr}   column_stackr`   rJ   )rI   	num_masksr   r   total_pixelsvertical_indiceshorizontal_indicesr   aggregation_axis
centroid_x
centroid_yr   r   r   calculate_masks_centroids  s   r   )r'   )NN)rq   )typingr   r   r   r   r   r   rZ   r   rJ   r   r&   floatrB   rF   rH   rW   rf   rn   rp   r{   r   dictr   r   r   r   r   r   r   r   <module>   sb    " 
"6
$
/%
4!