
    ^j=                       d dl mZ d dlZd dlZd dlmZ d dlmZ d dlZ	d dl
mZ d dlmZmZ d dlmZ d dlmZ d dlmZmZ d d	lmZ ej0                  Zd
Z G d de      Zej8                  f	 	 	 	 	 	 	 d dZ	 	 	 	 	 	 	 	 d!dZd"dZej8                  f	 	 	 	 	 	 	 	 	 d#dZ d$d%dZ!d&dZ" ee"dd      d&d       Z#	 	 	 	 	 	 d'dZ$	 	 	 	 	 	 d(dZ% G d d      Z&d)dZ'd*dZ(d+dZ)d,dZ*d,dZ+y)-    )annotationsN)Enum)Any)
deprecatedvoid)CLASS_NAME_DATA_FIELD)
Detections)ColorColorPalette)Positionc                  .    e Zd ZdZdZdZdZedd       Zy)ColorLookupa  
    Enumeration class to define strategies for mapping colors to annotations.

    This enum supports three different lookup strategies:
        - `INDEX`: Colors are determined by the index of the detection within the scene.
        - `CLASS`: Colors are determined by the class label of the detected object.
        - `TRACK`: Colors are determined by the tracking identifier of the object.
    indexclasstrackc                .    t        t        d |             S )Nc                    | j                   S N)value)cs    g/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/supervision/annotators/utils.py<lambda>z"ColorLookup.list.<locals>.<lambda>%   s
    !''     )listmap)clss    r   r   zColorLookup.list#   s    C)3/00r   N)return	list[str])	__name__
__module____qualname____doc__INDEXCLASSTRACKclassmethodr    r   r   r   r      s*     EEE1 1r   r   c                d   |t        |       k\  rt        d| dt        |              t        |t        j                        rHt        |      t        |       k7  r#t        dt        |       dt        |              t        ||         S |t        j                  k(  r|S |t        j                  k(  r/| j                  t        d      t        | j                  |         S |t        j                  k(  r/| j                  t        d      t        | j                  |         S t        d|       )NzDetection index z+ is out of bounds for detections of length zLength of color lookup z% does not match length of detections zCould not resolve color by class because Detections do not have class_id. If using an annotator, try setting color_lookup to sv.ColorLookup.INDEX or sv.ColorLookup.TRACK.zCould not resolve color by track because Detections do not have tracker_id. Did you call tracker.update_with_detections(...) before annotating?z#Unsupported color lookup strategy: )len
ValueError
isinstancenpndarrayintr   r$   r%   class_idr&   
tracker_id
detectionsdetection_idxcolor_lookups      r   resolve_color_idxr6   (   sK   
 J'}o .99<Z8IK
 	

 ,

+|J/)#l*;)< =77::6GI  <.//	**	*	**	*&(  :&&}566	**	*  (I 
 :((788
:<.I
JJr   c                   | \  }}|\  }}|t         j                  k(  r|||z
  ||z   |fS |t         j                  k(  r||z
  ||z
  ||fS |t         j                  k(  r||dz  z
  ||z
  ||dz  z   |fS |t         j                  k(  s|t         j
                  k(  r||dz  z
  ||dz  z
  ||dz  z   ||dz  z   fS |t         j                  k(  r||||z   ||z   fS |t         j                  k(  r||z
  ||||z   fS |t         j                  k(  r||dz  z
  |||dz  z   ||z   fS |t         j                  k(  r||z
  ||dz  z
  |||dz  z   fS |t         j                  k(  r|||dz  z
  ||z   ||dz  z   fS y )N   )r   TOP_LEFT	TOP_RIGHT
TOP_CENTERCENTERCENTER_OF_MASSBOTTOM_LEFTBOTTOM_RIGHTBOTTOM_CENTERCENTER_LEFTCENTER_RIGHT)center_coordinatestext_whpositioncenter_xcenter_ytext_wtext_hs          r   resolve_text_background_xyxyrJ   P   s   
 ,HhNFF8$$$F*Hv,=xGG	X''	'& (V"3XxGG	X((	(v{"vv{"	
 	
 
X__	$H4K4K(Kv{"v{"v{"v{"	
 	
 
X))	)8f#4h6GGG	X**	*& (Hh6GGG	X++	+v{"v{"v	
 	
 
X))	)vv{"v{"	
 	
 
X**	*v{"vv{"	
 	
 
+r   c                H    t        | t              r| j                  |      S | S r   )r,   r   by_idxcoloridxs     r   get_color_by_indexrP      s     %&||C  Lr   c                    t        |||      }t        |t              r"|t        j                  k(  r|t        k(  rt
        S t        | |      S )Nr2   rM   )r6   r,   r   r&   PENDING_TRACK_IDPENDING_TRACK_COLORrP   )rN   r3   r4   r5   rO   s        r   resolve_colorrT      sM     #!C 	<-K---##""Es33r   c                `   | sdgS t        | t              st        |       } || j                         xs dgS |dk  rt        d      | j	                  d      }g }|D ]I  }|dk(  r|j                  d       t        j                  ||ddd      }|j                  |xs dg       K |xs dgS )a.  
    Wrap `text` to the specified maximum line length, respecting existing
    newlines. Falls back to str() if `text` is not already a string.

    Args:
        text: The text (or object) to wrap.
        max_line_length: Maximum width for each wrapped line.

    Returns:
        Wrapped lines.
     r   z*max_line_length must be a positive integer
TF)widthbreak_long_wordsreplace_whitespacedrop_whitespace)	r,   str
splitlinesr+   splitappendtextwrapwrapextend)textmax_line_length
paragraphs	all_lines	paragraphwrappeds         r   	wrap_textri      s     tdC 4y (RD(!EFFD!JI *	?R --!!$ 
 	RD)* r   c                    | <t        |       t        |      k7  r$t        dt        |        dt        |       d      yy)ae  
    Validates that the number of provided labels matches the number of detections.

    Args:
        labels: A list of labels, one for each detection. Can
            be None.
        detections: The detections to be labeled.

    Raises:
        ValueError: If `labels` is not None and its length does not match the number
            of detections.
    NzThe number of labels (z+) does not match the number of detections (z.). Each detection should have exactly 1 label.)r*   r+   labelsr3   s     r   _validate_labelsrm      sR     c&kS_<$S[M 2%%(_$5 6+,
 	
 =r   z0.29.0z0.32.0)targetdeprecated_in	remove_inc                    t        | |       y r   )r   rk   s     r   validate_labelsrr      s     	r   c                b   ||S g }t        t        |             D ]  }t        | j                  v r/|j	                  t        | j                  t           |                D| j                  (|j	                  t        | j                  |                x|j	                  t        |              |S )a  
    Retrieves the text labels for the detections.

    If `custom_labels` are provided, they are used. Otherwise, the labels are
    extracted from the `detections` object, prioritizing the 'class_name' field,
    then the `class_id`, and finally using the detection index as a string.

    Args:
        detections: The detections to get labels for.
        custom_labels: An optional list of custom labels.

    Returns:
        A list of text labels for each detection.
    )ranger*   r   datar_   r\   r0   )r3   custom_labelsrl   rO   s       r   get_labels_textrw      s    "  FS_% $ JOO3MM#joo.CDSIJK  ,MM#j11#678MM#c(#$ Mr   c                   t        j                  |       }|\  }}|dddf   dk  }||dddfxx   ||ddf   z  cc<   | |dddf   |kD  z  }|||df   z
  }||dddfxx   |ddt         j                  f   z  cc<   |dddf   dk  }||dddfxx   ||ddf   z  cc<   | |dddf   |kD  z  }	|||	df   z
  }
||	dddfxx   |
ddt         j                  f   z  cc<   |j                  t         j                        S )a8  
    Shifts `label` bounding boxes into the frame so that they are fully contained
    within the given resolution, prioritizing the top/left edge.
    Unlike `clip_boxes`, this function does not crop boxes.
    It moves them entirely if they exceed the frame boundaries.

    Args:
        xyxy: 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: A tuple `(width, height)`
            representing the resolution of the frame.

    Returns:
        A numpy array of shape `(N, 4)` with boxes shifted into frame.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.annotators.utils import snap_boxes
        >>> xyxy = np.array([
        ...     [-10, 10, 30, 50],     # Off left edge
        ...     [310, 200, 350, 250],  # Off right edge
        ...     [100, -20, 150, 30],   # Off top edge
        ...     [200, 220, 250, 270],  # Off bottom edge
        ...     [-20, 10, 350, 50],    # Wider than frame (370 vs 320)
        ...     [10, -20, 30, 260]     # Taller than frame (280 vs 240)
        ... ])
        >>> resolution_wh = (320, 240)
        >>> snapped_boxes = snap_boxes(xyxy=xyxy, resolution_wh=resolution_wh)
        >>> snapped_boxes
        array([[  0.,  10.,  40.,  50.],
               [280., 190., 320., 240.],
               [100.,   0., 150.,  50.],
               [200., 190., 250., 240.],
               [  0.,  10., 370.,  50.],
               [ 10.,   0.,  30., 280.]], dtype=float32)

        ```
    Nr      r8         )r-   copynewaxisastypefloat32)xyxyresolution_whresultrX   heightleft_overflowright_overflowright_shifttop_overflowbottom_overflowbottom_shifts              r   
snap_boxesr   
  sR   X WWT]F!ME6 1a4L1$M
=!Aa% F=!A#+=$>> $n1)=>N&!233K
>1Qq5 ![BJJ%??! !Q$<!#L
<1Q6,!*;#<<$}1)>?OF?A#566L
?AaE!"l1bjj=&AA"==$$r   c                  N    e Zd Zddej                  f	 	 	 	 	 	 	 ddZddZddZy)	TraceNr   c                   || _         || _        || _        t        j                  g t
              | _        t        j                  dt        j                        | _	        t        j                  g t
              | _
        y )Ndtype)r   r8   )current_frame_idmax_sizeanchorr-   arrayr/   frame_idemptyr   xyr1   )selfr   start_frame_idr   s       r   __init__zTrace.__init__M  sT     !/ 3/((64((2S1r   c                J   t        j                  t        |      | j                  t              }t        j
                  | j                  |g      | _        t        j
                  | j                  |j                  | j                        g      | _        |j                  t        d      t        j
                  | j                  |j                  g      | _
        t        j                  | j                        }| j                  d| j                  cxk  rt        |      k  rjn ng| j                  | j                  z
  dz   }| j                  |k\  }| j                  |   | _        | j                  |   | _        | j                  |   | _
        | xj                  dz  c_        y )Nr   zNCould not put detections into Trace because Detections do not have tracker_id.r   rz   )r-   fullr*   r   r/   concatenater   r   get_anchors_coordinatesr   r1   r+   uniquer   )r   r3   r   unique_frame_idmax_allowed_frame_idfiltering_masks         r   putz	Trace.put[  sF   )+
OT22#*
 x'@A..224;;?
   (5 
 ..$//:;P;P)QR))DMM2==$T]])QS=Q)Q#'#8#84==#H1#L !]].BBN MM.9DMggn-DG"oon=DO"r   c                    | j                   | j                  |k(     j                         j                  t        j
                  d      }|S )NF)r|   )r   r1   r|   r~   r-   r   )r   r1   filtereds      r   getz	Trace.gety  sA    GGDOOz12779@@RW@X 	 r   )r   
int | Noner   r/   r   r   r   None)r3   r	   r   r   )r1   r/   r   %np.ndarray[Any, np.dtype[np.float32]])r    r!   r"   r   r<   r   r   r   r(   r   r   r   r   L  sF      $#??	22 2 	2
 
2#<r   r   c                d   | j                         j                  d      } t        |       dk(  r| dz  } t        |       dk7  rt        d|        	 t	        | dd d      }t	        | dd	 d      }t	        | d	d d      }t	        | dd d      }||||fS # t        $ r}t        d
|        |d}~ww xY w)z
    Converts a hex color string (e.g. "#FF00FF" or "#FF00FF80") to an RGBA tuple.

    Args:
        hex_color: A hex color string.

    Returns:
        RGBA values in range 0-255.

    Raises:
        ValueError: If the format is invalid.
    #   FF   zInvalid hex color format: r   r8      r{   zInvalid hex digits in N)striplstripr*   r+   r/   )	hex_colorrgbaexcs         r   hex_to_rgbar     s     !((-I
9~T	
9~5i[ABBH	!A#	!A#	!A#	!A# q!Q<  H1)=>CGHs   <B 	B/B**B/c                v    t        |       dk7  st        d | D              st        d       dj                  |  S )a  
    Converts an RGBA tuple (0-255 each) to a hex color string.

    Args:
        rgba: RGBA values in range 0-255.

    Returns:
        Hex color string in the format "#RRGGBBAA".

    Raises:
        ValueError: If `rgba` is not a 4-tuple or contains values outside 0-255.
    r{   c              3  <   K   | ]  }d |cxk  xr dk  nc   yw)r      Nr(   ).0r   s     r   	<genexpr>zrgba_to_hex.<locals>.<genexpr>  s      =1a3 =s   z1RGBA must be a 4-tuple with values between 0-255.z#{:02X}{:02X}{:02X}{:02X})r*   allr+   format)rgbas    r   rgba_to_hexr     s<     4yA~S = ==LMM-&--t44r   c                \    t        t        j                  d| j                                     S )a#  
    Checks if a given string is a valid hex color.

    Args:
        hex_color: A hex color string with an optional leading "#". Supports
            6-digit (RGB) or 8-digit (RGBA) formats.

    Returns:
        True if the string is a valid 6- or 8-digit hex color, otherwise False.
    z!#?[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?)boolre	fullmatchr   )r   s    r   is_valid_hexr     s"     A9??CTUVVr   c                @    t        dt        ||z
  || z
        dz        S )a  
    Computes a blur kernel size proportional to the shorter side of a bounding box.

    Args:
        x1: Left edge of the bounding box.
        y1: Top edge of the bounding box.
        x2: Right edge of the bounding box.
        y2: Bottom edge of the bounding box.

    Returns:
        Kernel size as one-third of the shorter dimension, minimum 1.

    Examples:
        ```pycon
        >>> calculate_dynamic_kernel_size(0, 0, 90, 60)
        20

        ```
    rz   ry   maxminx1y1x2y2s       r   calculate_dynamic_kernel_sizer     %    ( q#b2grBw'1,--r   c                @    t        dt        ||z
  || z
        dz        S )a  
    Computes a pixelation size proportional to the shorter side of a bounding box.

    Args:
        x1: Left edge of the bounding box.
        y1: Top edge of the bounding box.
        x2: Right edge of the bounding box.
        y2: Bottom edge of the bounding box.

    Returns:
        Pixel size as one-half of the shorter dimension, minimum 1.

    Examples:
        ```pycon
        >>> calculate_dynamic_pixel_size(0, 0, 90, 60)
        30

        ```
    rz   r8   r   r   s       r   calculate_dynamic_pixel_sizer     r   r   )r3   r	   r4   r/   r5   "ColorLookup | npt.NDArray[np.int_]r   r/   )rC   tuple[int, int]rD   r   rE   r   r   tuple[int, int, int, int])rN   Color | ColorPaletterO   r/   r   r
   )
rN   r   r3   r	   r4   r/   r5   r   r   r
   r   )rc   r   rd   r   r   r   )rl   list[str] | Noner3   r	   r   r   )r3   r	   rv   r   r   r   )r   r   r   r   r   r   )r   r\   r   r   )r   r   r   r\   )r   r\   r   r   )
r   r/   r   r/   r   r/   r   r/   r   r/   ),
__future__r   r   r`   enumr   typingr   numpyr-   numpy.typingnpt	deprecater   r   supervision.configr   supervision.detection.corer	   supervision.draw.colorr
   r   supervision.geometry.corer   GREYrS   rR   r   r%   r6   rJ   rP   rT   ri   rm   rr   rw   r   r   r   r   r   r   r   r(   r   r   <module>r      s}   " 	      & 4 1 6 .jj  1$ 1, 8C7H7H%K%K%K 5%K 		%KP2
'2
2
 2
 	2
j 8C7H7H	444 4 5	4
 4(+\
* 

+;>?%
/?%"?% +?%D1 1h85$W...r   