
    ^jo                        d dl mZ d dlmZ d dlmZ d dlmZm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 d dlmZmZmZ d dlmZm Z  d dl!m"Z" erd dl#Z$ G d de      Z%e G d d             Z&y)    )annotations)deepcopy)	dataclass)TYPE_CHECKINGAnyN)pyplot)ORIENTED_BOX_COORDINATES)
Detections)box_iou_batchmask_iou_batchoriented_box_iou_batch)LEGACY_COLOR_PALETTE)AveragingMethodMetricMetricTarget)ObjectSizeCategoryget_detection_size_category)ensure_pandas_installedc                  B   e Zd ZdZej
                  ej                  f	 	 	 ddZddZ		 	 	 	 	 	 ddZ
ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 ddZe	 	 	 	 	 	 	 	 	 	 dd       Ze	 	 	 	 	 	 	 	 	 	 dd	       Ze	 	 	 	 dd
       ZddZddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 ddZy)F1Scorea  
    F1 Score is a metric used to evaluate object detection models. It is the harmonic
    mean of precision and recall, calculated at different IoU thresholds.

    In simple terms, F1 Score is a measure of a model's balance between precision and
    recall (accuracy and completeness), calculated as:

    `F1 = 2 * (precision * recall) / (precision + recall)`

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> from supervision.metrics import F1Score
        >>> predictions = sv.Detections(
        ...     xyxy=np.array([[0, 0, 10, 10]]),
        ...     class_id=np.array([0]),
        ...     confidence=np.array([0.9])
        ... )
        >>> targets = sv.Detections(
        ...     xyxy=np.array([[0, 0, 10, 10]]),
        ...     class_id=np.array([0])
        ... )
        >>> f1_metric = F1Score()
        >>> f1_result = f1_metric.update(predictions, targets).compute()
        >>> round(float(f1_result.f1_50), 2)
        1.0

        ```

    ![example_plot](
        https://media.roboflow.com/supervision-docs/metrics/f1_plot_example.png
    ){ align=center width="800" }
    c                <    || _         || _        g | _        g | _        y)a  
        Initialize the F1Score metric.

        Args:
            metric_target: The type of detection data to use.
            averaging_method: The averaging method used to compute the
                F1 scores. Determines how the F1 scores are aggregated across classes.
        N)_metric_targetaveraging_method_predictions_list_targets_list)selfmetric_targetr   s      g/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/supervision/metrics/f1_score.py__init__zF1Score.__init__B   s$     , 035/1    c                     g | _         g | _        y)zR
        Reset the metric to its initial state, clearing all stored data.
        N)r   r   r   s    r   resetzF1Score.resetU   s     "$r    c                4   t        |t              s|g}t        |t              s|g}t        |      t        |      k7  r$t        dt        |       dt        |       d      | j                  j                  |       | j                  j                  |       | S )a  
        Add new predictions and targets to the metric, but do not compute the result.

        Args:
            predictions: The predicted detections.
            targets: The target detections.

        Returns:
            The updated metric instance.
        zThe number of predictions (z) and targets (z%) during the update must be the same.)
isinstancelistlen
ValueErrorr   extendr   )r   predictionstargetss      r   updatezF1Score.update\   s     +t,&-K'4(iG{s7|+-c+.>-? @ \N*OQ 
 	%%k2!!'*r    c                ,   | j                  | j                  | j                        }| j                  | j                  | j                  t        j
                        \  }}| j                  ||      |_        | j                  | j                  | j                  t        j                        \  }}| j                  ||      |_        | j                  | j                  | j                  t        j                        \  }}| j                  ||      |_
        |S )z
        Calculate the F1 score metric based on the stored predictions and ground-truth
        data, at different IoU thresholds.

        Returns:
            The F1 score metric result.
        )_computer   r   '_filter_predictions_and_targets_by_sizer   SMALLsmall_objectsMEDIUMmedium_objectsLARGElarge_objects)r   resultsmall_predictionssmall_targetsmedium_predictionsmedium_targetslarge_predictionslarge_targetss           r   computezF1Score.compute{   s     t55t7I7IJ+/+W+W""D$6$68J8P8P,
(=  $}}->N 88&&(:(:<N<U<U 	+N
 !%.@. Q+/+W+W""D$6$68J8P8P,
(=  $}}->Nr    c                ~   t        j                  ddd      }g }t        ||      D ]  \  }}| j                  |      }| j                  |      }t	        |      dk(  rt	        |      dkD  r|j
                  |j                  ^|j                  t        j                  t	        |      |j                  ft         j                        |j                  |j
                  t        j                  dt         j                        f       t	        |      dkD  st	        |      dk(  r|j                  t        j                  d|j                  ft              t        j                  dt         j                        t        j                  dt              |j
                  f       | j                  t         j"                  k(  rt%        ||      }	n_| j                  t         j&                  k(  rt)        ||      }	n5| j                  t         j*                  k(  rt-        ||      }	nt/        d      | j1                  |j
                  |j
                  nt        j2                  g       |j
                  |j
                  nt        j2                  g       |	|      }
|j                  |
|j                  |j
                  |j
                  f        |st5        | j                  | j6                  t        j                  |j8                  d         t        j                  d|j8                  d   f      |t        j2                  g t              ddd		      S t        | D cg c]  }t        j:                  |d       }} | j<                  | \  }}}t5        | j                  | j6                  ||||ddd		      S c c}w )
a  Build per-image stats tuples and delegate to class-level computation.

        Each stats tuple is ``(matches, confidence, class_ids, true_class_ids)``:
        - Both empty: skip (no information).
        - Targets empty, predictions present: all predictions are FPs; true_class_ids
          is ``zeros((0,))``.
        - Targets present: IoU matching produces ``matches`` array.
        g      ?gffffff?
   r   Ndtype)r   z-Unsupported metric target for IoU calculation)	r   r   	f1_scoresf1_per_classiou_thresholdsmatched_classesr1   r3   r5   )nplinspacezip_detections_contentr'   class_id
confidenceappendzerossizebool_int32boolfloat32intr   r   BOXESr   MASKSr   ORIENTED_BOUNDING_BOXESr   r(   _match_detection_batcharrayF1ScoreResultr   shapeconcatenate_compute_f1_for_classes)r   predictions_listtargets_listrD   statsr*   r+   prediction_contentstarget_contentsioumatchesitemsconcatenated_statsrB   rC   unique_classess                   r   r.   zF1Score._compute   sO    S$3$'(8,$G =	 K"&":":;"G"66w?O7|q S%5%9 ''/;3I3I3Q -~/B/BC288 $..#,,RXX6	 W!{#q(LLHHa)<)<%=TJHHT<HHT5#,,	 **l.@.@@+O=PQ,,0B0BB,_>QR,,0T0TT4+-@ )K  #99&//; $,,XXb\"++7  ((XXb\&	G LL#'22'00#,,	m=	~  "11!%!6!6((>#7#7#:;XXq.*>*>q*A&BC- "3 7"#"
 
 EHKP5bnnUA6PP2N$2N2N3
/	< --!22%)*

 
	
 Qs    N:c                   t        j                  |       }||   }||   }t        j                  t        j                  ||f            }t        j                  |d      \  }}t        j                  |j
                  d   t              }	||	t        j                  ||      <   | j                  ||||	      }
| j                  |
      }| j                  t        j                  k(  rt        j                  |d      }n| j                  t        j                  k(  r#|
j                  d      }| j                  |      }n| j                  t        j                   k(  rm|	j#                  t         j$                        }	|	j                         dk(  r#t        j                  |j
                  d         }nt        j&                  |d|	      }||fS )zCompute F1 scores from concatenated stats across all images.

        ``unique_classes`` is the union of GT and predicted classes so that
        predictions of classes absent from GT still count as false positives.
        T)return_countsr   r@   axis   )rj   weights)rF   argsortuniquer[   rM   rZ   rS   searchsorted_compute_confusion_matrix_compute_f1r   r   MACROmeanMICROsumWEIGHTEDastyperR   average)r   rc   prediction_confidenceprediction_class_idstrue_class_idssorted_indicesrf   true_classestrue_countsclass_countsconfusion_matrixrC   rB   confusion_matrix_mergeds                 r   r\   zF1Score._compute_f1_for_classes  s     %:$:;.)3NC NNN,@AB
 %'IInD$Q!kxx 4 4Q 7sCFQR__^\BC  99)><

 ''(89   O$9$9915I""o&;&;;&6&:&:1&=#(()@AI""o&>&>>'..rzz:L!Q& HH\%7%7%:;	JJ|!\R	,66r    c                   | j                   d   |j                   d   }}t        j                  ||ft              }|d d d f   | k(  }t	        |      D ]  \  }}	t        j
                  ||	k\  |z        }
|
d   j                   d   s5t        j                  |
d      }||
   d d d f   }t        j                  ||g      }|
d   j                   d   dkD  rh||d d df   j                         d d d      }|t        j                  |d d df   d      d      }|t        j                  |d d df   d      d      }d||d d df   j                  t              |f<    |}|S )	Nr   r@   rk   ri      T)return_index)rZ   rF   rM   rQ   	enumeratewherestackhstackrm   rn   rw   rS   )predictions_classestarget_classesrb   rD   num_predictionsnum_iou_levelscorrectcorrect_classi	iou_levelmatched_indicescombined_indices
iou_valuesrc   result_corrects                  r   rW   zF1Score._match_detection_batch:  s~     %%a(  # ( ((O^<DI&q$w/3FF%n5 	=LAy hhy(8M'IJOq!''*#%88O!#D  1!T':
))%5z$BC"1%++A.2%gadm&;&;&=dd&CDG%bii1D&QRS&TUG%bii1D&QRS&TUG8<1,,S1145	= 18r    c                   | j                   d   }|j                   d   }t        j                  ||dft        j                        }t	        |      D ]  \  }}||k(  }	||   }
|	j                         }|dk(  rAt        j                  |      }t        j                  |      }t        j                  ||
      }nv|
dk(  rAt        j                  |      }t        j                  ||      }t        j                  |      }n0| |	   j                  d      }d| |	   z
  j                  d      }|
|z
  }t        j                  |||gd      ||<    |}|S )a3  
        Compute the confusion matrix for each class and IoU threshold.

        Assumes the matches and prediction_class_ids are sorted by confidence
        in descending order.

        Args:
            sorted_matches: shape (P, Th), that is True
                if the prediction is a true positive at the given IoU threshold.
            sorted_prediction_class_ids: shape (P,), containing
                the class id for each prediction.
            unique_classes: shape (C,), containing the unique
                class ids.
            class_counts: shape (C,), containing the number
                of true instances for each class.

        Returns:
            shape (C, Th, 3), containing the true positives, false
                positives, and false negatives for each class and IoU threshold.
        rk   r      r@   ri   )rZ   rF   rM   float64r   ru   fullr   )sorted_matchessorted_prediction_class_idsrf   r   num_thresholdsnum_classesr   	class_idxrJ   is_classnum_truer   true_positivesfalse_positivesfalse_negativesresult_confusion_matrixs                   r   rp   z!F1Score._compute_confusion_matrixZ  sL   8 (--a0$**1-46HH.!,BJJ5
 $-^#< 	Ix2h>H#I.H&llnO!#!#.!9"$((>":"$''.("CQ!#.!9"$''./"J"$((>":!/!9!=!=a!@#$~h'?#?"D"DQ"G"*^";*,((/B+Y'#	* <L&&r    c                    | j                   d   dk(  st        d| j                          | d   }| d   }| d   }d|z  |z   |z   }t        j                  |dk(  dd|z  |z        }|}|S )	a_  
        Broadcastable function, computing the F1 score from the confusion matrix.

        Args:
            confusion_matrix: shape (N, ..., 3), where the last dimension
                contains the true positives, false positives, and false negatives.

        Returns:
            shape (N, ...), containing the F1 score for each element.
        r   r   z/Confusion matrix must have shape (..., 3), got ).r   ).rk   ).r   r   r   )rZ   r(   rF   r   )r   r   r   r   denominatorf1_scoreresult_f1_scores          r   rq   zF1Score._compute_f1  s      %%b)Q.A#))*,  *&1*62*62 .(?:_L88K1,a^1Ck1QR3;r    c                   | j                   t        j                  k(  r|j                  }|S | j                   t        j                  k(  r*|j
                  |j
                  }|S | j                         S | j                   t        j                  k(  rf|j                  j                  t              }|5t        |      dkD  r't        j                  |t        j                        }|S | j                         S t        d| j                          )z?Return boxes, masks or oriented bounding boxes from detections.r   r@   Invalid metric target: )r   r   rT   xyxyrU   mask_make_empty_contentrV   datagetr	   r'   rF   rX   rR   r(   )r   
detectionsresult_boxesresult_masksobb
result_obbs         r   rI   zF1Score._detections_content  s    ,"4"444>OOL,"4"44*6@oo##++--,"F"FF//%%&>?C3s8a<68hhs"**6U
!!++--243F3F2GHIIr    c                   | j                   t        j                  k(  r't        j                  dt        j
                        }|S | j                   t        j                  k(  rt        j                  dt              }|S | j                   t        j                  k(  r't        j                  dt        j
                        }|S t        d| j                          )N)r      r@   )r   r   r   )r   r   r   r   )
r   r   rT   rF   emptyrR   rU   rQ   rV   r(   )r   empty_boxesempty_masks	empty_obbs       r   r   zF1Score._make_empty_content  s    ,"4"443588F"**3UK,"4"4413)41PK,"F"FF13)2::1VI243F3F2GHIIr    c                ~   t        |      }|j                         s|t        j                  k(  r|S t	        || j
                        }||j                  k(  }|j                  |   |_        |j                  |j                  |   |_        |j                  |j                  |   |_	        |j                  |j                  |   |_
        |j                  |j                  |   |_        |j                  G|j                  j                         D ]*  \  }}t        j                  |      |   |j                  |<   , |S )zBReturn a copy of detections with contents filtered by object size.)r   is_emptyr   ANYr   r   valuer   r   rJ   rK   
tracker_idr   rd   rF   rX   )r   r   size_categorynew_detectionssizes	size_maskkeyr   s           r   _filter_detections_by_sizez"F1Score._filter_detections_by_size  s4    "*- M5G5K5K$K!!+ND<O<OP]000	,11)<*"0"5"5i"@N"".&4&=&=i&HN#$$0(6(A(A)(LN%$$0(6(A(A)(LN%*,11779 F
U+-88E?9+E##C(F r    c                    g }g }t        ||      D ]G  \  }}|j                  | j                  ||             |j                  | j                  ||             I ||fS )zI
        Filter predictions and targets by object size category.
        )rH   rL   r   )r   r]   r^   r   new_predictions_listnew_targets_listr*   r+   s           r   r/   z/F1Score._filter_predictions_and_targets_by_size  sx      "$'(8,$G 	 K ''//]K ##//G		 $%555r    N)r   r   r   r   returnNone)r*   Detections | list[Detections]r+   r   r   r   )r   rY   )r]   list[Detections]r^   r   r   rY   )
rc   npt.NDArray[np.bool_]ry   npt.NDArray[np.float32]rz   npt.NDArray[np.int32]r{   r   r   zNtuple[npt.NDArray[np.float64], npt.NDArray[np.float64], npt.NDArray[np.int32]])
r   r   r   r   rb   r   rD   r   r   r   )
r   r   r   r   rf   r   r   r   r   npt.NDArray[np.float64])r   r   r   r   )r   r
   r   npt.NDArray[Any])r   r   )r   r
   r   r   r   r
   )r]   r   r^   r   r   r   r   z)tuple[list[Detections], list[Detections]])__name__
__module____qualname____doc__r   rT   r   rv   r   r#   r,   r=   r.   r\   staticmethodrW   rp   rq   rI   r   r   r/    r    r   r   r      s   !J '3&8&8,;,D,D2#2 *2& 2 / 
	>:i
 0i
@Pi
	i
V57&57  757 4	57
 .57
57n 2- % 0	
 
 > 7'-7'%:7' .7' ,	7'
 
!7' 7'r 1	  :J$
J$5G	46*6 '6 *	6
 
36r    r   c                      e Zd ZU dZded<   ded<   edd       Zedd       Zded	<   ded
<   ded<   ded<   ded<   ded<   ded<   ddZddZ	ddZ
y)rY   ua  
    The results of the F1 score metric calculation.

    Defaults to `0` if no detections or targets were provided.

    Attributes:
        metric_target: the type of data used for the metric -
            boxes, masks or oriented bounding boxes.
        averaging_method: the averaging method used to compute the
            F1 scores. Determines how the F1 scores are aggregated across classes.
        f1_50: the F1 score at IoU threshold of `0.5`.
        f1_75: the F1 score at IoU threshold of `0.75`.
        f1_scores: the F1 scores at each IoU threshold.
            Shape: `(num_iou_thresholds,)`
        f1_per_class: the F1 scores per class and IoU threshold.
            Shape: `(num_classes, num_iou_thresholds)`
        iou_thresholds: the IoU thresholds used in the calculations.
        matched_classes: the class IDs present in either predictions or ground
            truth. Corresponds to the rows of `f1_per_class`. Classes that
            appear only in predictions (no ground-truth instances) are
            included; their per-threshold F1 values will be `0.0`.
        small_objects: the F1 metric results
            for small objects (area < 32²).
        medium_objects: the F1 metric results
            for medium objects (32² ≤ area < 96²).
        large_objects: the F1 metric results
            for large objects (area ≥ 96²).
    r   r   r   r   c                2    t        | j                  d         S )Nr   floatrB   r"   s    r   f1_50zF1ScoreResult.f1_50!      T^^A&''r    c                2    t        | j                  d         S )N   r   r"   s    r   f1_75zF1ScoreResult.f1_75%  r   r    r   rB   rC   r   rD   r   rE   zF1ScoreResult | Noner1   r3   r5   c                   | j                   j                   d| j                   d| j                   d| j                  dd| j
                  dd| j                   d| j                   d}| j                  j                  d	k(  r|d
z  }t        | j                  | j                        D ]  \  }}|d| d| dz  } d}| j                  3|t        | j                        j                  dd|       z   }|d| z  }| j                  3|t        | j                        j                  dd|       z   }|d| z  }| j                   3|t        | j                         j                  dd|       z   }|d| z  }|S )a,  
        Format as a pretty string.

        Example:
            ```pycon
            >>> import numpy as np
            >>> import supervision as sv
            >>> from supervision.metrics import F1Score
            >>> predictions = sv.Detections(
            ...     xyxy=np.array([[0, 0, 10, 10]]),
            ...     class_id=np.array([0]),
            ...     confidence=np.array([0.9])
            ... )
            >>> targets = sv.Detections(
            ...     xyxy=np.array([[0, 0, 10, 10]]),
            ...     class_id=np.array([0])
            ... )
            >>> f1_metric = F1Score()
            >>> f1_result = f1_metric.update(predictions, targets).compute()
            >>> print(f1_result)  # doctest: +ELLIPSIS
            F1ScoreResult:
            Metric target: MetricTarget.BOXES
            Averaging method: AveragingMethod.WEIGHTED
            F1 @ 50:     1.0000
            F1 @ 75:     1.0000
            F1 @ thresh: [1. ... 1.]
            IoU thresh:  [0.5  0.55 ... 0.95]
            F1 per class:
              0: [1. ... 1.]
            ...
            Medium objects:
              F1ScoreResult:
              Metric target: MetricTarget.BOXES
              Averaging method: AveragingMethod.WEIGHTED
              F1 @ 50:     0.0000
              ...

            ```
        z:
Metric target: z
Averaging method: z
F1 @ 50:     z.4fz
F1 @ 75:     z
F1 @ thresh: z
IoU thresh:  z
F1 per class:
r   z  No results
z  z: 
z
Small objects:
z
Medium objects:
z
Large objects:
)	__class__r   r   r   r   r   rB   rD   rC   rN   rH   rE   r1   strreplacer3   r5   )r   out_strrJ   f1_of_classindentindenteds         r   __str__zF1ScoreResult.__str__2  s   R ~~&&' ("001 2!!%!6!6 7 8 JJs+ , JJs+ , NN+ , //0 1 	 !!Q&''G%()=)=t?P?P%Q 	8!HkH:R}B77G	8 )D$6$6 7 ? ?6(m TTH+H:66G*D$7$7 8 @ @F8} UUH,XJ77G)D$6$6 7 ? ?6(m TTH+H:66Gr    c                   t                ddl}| j                  | j                  d}| j                  :| j                  j                         }|j                         D ]  \  }}||d| <    | j                  :| j                  j                         }|j                         D ]  \  }}||d| <    | j                  :| j                  j                         }|j                         D ]  \  }}||d| <     |j                  |dg      S )zt
        Convert the result to a pandas DataFrame.

        Returns:
            The result as a DataFrame.
        r   N)F1@50F1@75small_objects_medium_objects_large_objects_)index)
r   pandasr   r   r1   	to_pandasrd   r3   r5   	DataFrame)r   pdpandas_datasmall_objects_dfr   r   medium_objects_dflarge_objects_dfs           r   r   zF1ScoreResult.to_pandasv  s'    	 ! ZZZZ

 )#11;;=.446 <
U6;nSE23<* $ 3 3 = = ?/557 =
U7<ocU34=)#11;;=.446 <
U6;nSE23< r||Ks33r    c                   ddg}| j                   | j                  g}t        d   gdz  }| j                  >| j                  }|ddgz  }||j                   |j                  gz  }|t        d   gdz  z  }| j                  >| j                  }|d	d
gz  }||j                   |j                  gz  }|t        d   gdz  z  }| j
                  >| j
                  }|ddgz  }||j                   |j                  gz  }|t        d   gdz  z  }dt        j                  d<   t        j                  d      \  }}|j                  dd       |j                  dd       d| j                  j                   d| j                  j                   d}	|j                  |	d       t        t!        |            }
|j#                  |
||d      }|j%                  |
       |j'                  |dd       |D ]O  }|j)                         }|j+                  |j-                         |j/                         dz  z   |dz   |ddd !       Q d"t        j                  d<   t        j0                          t        j2                          y)#z
        Plot the F1 results.

        ![example_plot](
            https://media.roboflow.com/supervision-docs/metrics/f1_plot_example.png
        ){ align=center width="800" }
        r   r   r   r   NzSmall: F1@50zSmall: F1@75r   zMedium: F1@50zMedium: F1@75zLarge: F1@50zLarge: F1@75r   	monospacezfont.family)r?      )figsizerk   Valuebold)
fontweightz"F1 Score, by Object Size
(target: z, averaging: )center)coloralign-   right)rotationhag{Gz?z.2fbottom)r	  vaz
sans-serif)r   r   r   r1   r3   r5   pltrcParamssubplotsset_ylim
set_ylabelr   r   r   	set_titleranger'   bar
set_xticksset_xticklabels
get_heighttextget_x	get_widthtight_layoutshow)r   labelsvaluescolorsr1   r3   r5   _axtitlex_positionsbarsr  y_values                 r   plotzF1ScoreResult.plot  s    7#**djj)&q)*Q.) ..M~~66F}**M,?,?@@F+A./!33F*!00N88F~++^-A-ABBF+A./!33F) ..M~~66F}**M,?,?@@F+A./!33F&1]#W-2
Aq
g&1,,223 400667q: 	
 	Uv.CK(vvk6xvH
k"
6B7; 	Cnn&GGG		cmmo11$3-  	 '3]#
r    N)r   r   )r   r   )r   zpd.DataFramer   )r   r   r   r   __annotations__propertyr   r   r   r   r%  r   r    r   rY   rY     sx    :  %%( ( ( ( '&))++**''((''BH4<>r    rY   )'
__future__r   copyr   dataclassesr   typingr   r   numpyrF   numpy.typingnpt
matplotlibr   r  supervision.configr	   supervision.detection.corer
   'supervision.detection.utils.iou_and_nmsr   r   r   supervision.draw.colorr   supervision.metrics.corer   r   r   %supervision.metrics.utils.object_sizer   r   supervision.metrics.utils.utilsr   r   r   r   rY   r   r    r   <module>r7     sq    "  ! %   $ 7 1 
 8 J J D^6f ^6B R R Rr    