o
    ez                     @  s   d dl mZ d dlmZ d dlmZmZmZmZ d dl	Z	d dl
mZ d dlZd dlmZ d dlmZ d dlmZ 	ddddZdddZeG dd dZeddG dd dZdS )    )annotations)	dataclass)CallableListOptionalTupleN)DetectionDataset)
Detections)box_iou_batchF
detectionsr	   with_confidenceboolreturn
np.ndarrayc                 C  s^   | j du r	td| jt| j dg}|r(| jdu rtd|t| jd tj|ddS )aq  
    Convert Supervision Detections to numpy tensors for further computation
    Args:
        detections (sv.Detections): Detections/Targets in the format of sv.Detections
        with_confidence (bool): Whether to include confidence in the tensor
    Returns:
        (np.ndarray): Detections as numpy tensors as in (xyxy, class_id,
            confidence) order
    NzCConfusionMatrix can only be calculated for Detections with class_id   zEConfusionMatrix can only be calculated for Detections with confidenceaxis)class_id
ValueErrorxyxynpexpand_dims
confidenceappendconcatenate)r   r   arrays_to_concat r   L/var/www/myenv/lib/python3.10/site-packages/supervision/metrics/detection.pydetections_to_tensor   s   

r   predictionsList[np.ndarray]targetsc                 C  s   t | t |krtdt |  dt | dt | dkrht| d tjr-t|d tjs@tdt| d  dt|d  d| d jd d	krTtd
| d j d|d jd dkrjtd|d j ddS dS )z8
    Checks for shape consistency of input tensors.
    zNumber of predictions (z) andtargets (z) must be equal.r   z:Predictions and targets must be lists of numpy arrays.Got z and z	 instead.r      z'Predictions must have shape (N, 6).Got    z$Targets must have shape (N, 5). Got N)lenr   
isinstancer   ndarraytypeshaper   r!   r   r   r   validate_input_tensors,   s:   


r*   c                   @  s   e Zd ZU dZded< ded< ded< ded< e			
d/d0ddZe			
d/d1ddZed2ddZ	ed3ddZ
e			
d/d4dd Z	!	!	!	"	#d5d6d-d.Zd!S )7ConfusionMatrixac  
    Confusion matrix for object detection tasks.

    Attributes:
        matrix (np.ndarray): An 2D `np.ndarray` of shape
            `(len(classes) + 1, len(classes) + 1)`
            containing the number of `TP`, `FP`, `FN` and `TN` for each class.
        classes (List[str]): Model class names.
        conf_threshold (float): Detection confidence threshold between `0` and `1`.
            Detections with lower confidence will be excluded from the matrix.
        iou_threshold (float): Detection IoU threshold between `0` and `1`.
            Detections with lower IoU will be classified as `FP`.
    r   matrix	List[str]classesfloatconf_thresholdiou_threshold333333?      ?r   List[Detections]r!   r   c           
      C  sT   g }g }t ||D ]\}}	|t|dd |t|	dd q	| j|||||dS )a  
        Calculate confusion matrix based on predicted and ground-truth detections.

        Args:
            targets (List[Detections]): Detections objects from ground-truth.
            predictions (List[Detections]): Detections objects predicted by the model.
            classes (List[str]): Model class names.
            conf_threshold (float): Detection confidence threshold between `0` and `1`.
                Detections with lower confidence will be excluded.
            iou_threshold (float): Detection IoU threshold between `0` and `1`.
                Detections with lower IoU will be classified as `FP`.

        Returns:
            ConfusionMatrix: New instance of ConfusionMatrix.

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

            >>> targets = [
            ...     sv.Detections(...),
            ...     sv.Detections(...)
            ... ]

            >>> predictions = [
            ...     sv.Detections(...),
            ...     sv.Detections(...)
            ... ]

            >>> confusion_matrix = sv.ConfusionMatrix.from_detections(
            ...     predictions=predictions,
            ...     targets=target,
            ...     classes=['person', ...]
            ... )

            >>> confusion_matrix.matrix
            array([
                [0., 0., 0., 0.],
                [0., 1., 0., 1.],
                [0., 1., 1., 0.],
                [1., 1., 0., 0.]
            ])
            ```
        Tr   Fr   r!   r.   r0   r1   zipr   r   from_tensors)
clsr   r!   r.   r0   r1   prediction_tensorstarget_tensors
predictiontargetr   r   r   from_detections]   s   6
zConfusionMatrix.from_detectionsr    c           
   	   C  sd   t || t|}t|d |d f}t||D ]\}}	|| j|	||||d7 }q| ||||dS )a	  
        Calculate confusion matrix based on predicted and ground-truth detections.

        Args:
            predictions (List[np.ndarray]): Each element of the list describes a single
                image and has `shape = (M, 6)` where `M` is the number of detected
                objects. Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class, conf)` format.
            targets (List[np.ndarray]): Each element of the list describes a single
                image and has `shape = (N, 5)` where `N` is the number of
                ground-truth objects. Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class)` format.
            classes (List[str]): Model class names.
            conf_threshold (float): Detection confidence threshold between `0` and `1`.
                Detections with lower confidence will be excluded.
            iou_threshold (float): Detection iou  threshold between `0` and `1`.
                Detections with lower iou will be classified as `FP`.

        Returns:
            ConfusionMatrix: New instance of ConfusionMatrix.

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

            >>> targets = (
            ...     [
            ...         array(
            ...             [
            ...                 [0.0, 0.0, 3.0, 3.0, 1],
            ...                 [2.0, 2.0, 5.0, 5.0, 1],
            ...                 [6.0, 1.0, 8.0, 3.0, 2],
            ...             ]
            ...         ),
            ...         array([1.0, 1.0, 2.0, 2.0, 2]),
            ...     ]
            ... )

            >>> predictions = [
            ...     array(
            ...         [
            ...             [0.0, 0.0, 3.0, 3.0, 1, 0.9],
            ...             [0.1, 0.1, 3.0, 3.0, 0, 0.9],
            ...             [6.0, 1.0, 8.0, 3.0, 1, 0.8],
            ...             [1.0, 6.0, 2.0, 7.0, 1, 0.8],
            ...         ]
            ...     ),
            ...     array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
            ... ]

            >>> confusion_matrix = sv.ConfusionMatrix.from_tensors(
            ...     predictions=predictions,
            ...     targets=targets,
            ...     classes=['person', ...]
            ... )

            >>> confusion_matrix.matrix
            array([
                [0., 0., 0., 0.],
                [0., 1., 0., 1.],
                [0., 1., 1., 0.],
                [1., 1., 0., 0.]
            ])
            ```
        r   )r   r!   num_classesr0   r1   )r,   r.   r0   r1   )r*   r$   r   zerosr8   evaluate_detection_batch)
r:   r   r!   r.   r0   r1   r@   r,   
true_batchdetection_batchr   r   r   r9      s"   
J
zConfusionMatrix.from_tensorsr@   intc                 C  s  t |d |d f}d}| dd|f }| ||k }d}	t j|dd|	f t jd}
t j|dd|	f t jd}|ddd|	f }|ddd|	f }t||d}t ||k }|d jd rzt j|d |d || fdd}t	j
|d	}nt d
}| t j\}}}t|
D ].\}}||k}|jd dkrt|dkr|||||  f  d7  < q|||f  d7  < qt|D ]\}}t||ks|||f  d7  < q|S )aP  
        Calculate confusion matrix for a batch of detections for a single image.

        Args:
            predictions (np.ndarray): Batch prediction. Describes a single image and
                has `shape = (M, 6)` where `M` is the number of detected objects.
                Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class, conf)` format.
            targets (np.ndarray): Batch target labels. Describes a single image and
                has `shape = (N, 5)` where `N` is the number of ground-truth objects.
                Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class)` format.
            num_classes (int): Number of classes.
            conf_threshold (float): Detection confidence threshold between `0` and `1`.
                Detections with lower confidence will be excluded.
            iou_threshold (float): Detection iou  threshold between `0` and `1`.
                Detections with lower iou will be classified as `FP`.

        Returns:
            np.ndarray: Confusion matrix based on a single image.
        r   r#   N   dtype)
boxes_trueboxes_detectionr   r   matches)r      )r   rA   arrayint16r
   asarraynonzeror(   stackr+   _drop_extra_matches	transposeastype	enumeratesumany)r   r!   r@   r0   r1   result_matrixconf_idxr   detection_batch_filteredclass_id_idxtrue_classesdetection_classes
true_boxesdetection_boxes	iou_batchmatched_idxrL   matched_true_idxmatched_detection_idx_itrue_class_valuejdetection_class_valuer   r   r   rB      sL   

z(ConfusionMatrix.evaluate_detection_batchrL   c                 C  s   | j d dkrK| | dddf  ddd  } | tj| dddf ddd  } | | dddf  ddd  } | tj| dddf ddd  } | S )z
        Deduplicate matches. If there are multiple matches for the same true or
        predicted box, only the one with the highest IoU is kept.
        r   N   r   Treturn_index)r(   argsortr   uniquerK   r   r   r   rS   J  s   """"z#ConfusionMatrix._drop_extra_matchesdatasetr   callback"Callable[[np.ndarray], Detections]c                 C  sZ   g g }}|j  D ]\}}||}	||	 |j| }
||
 q
| j|||j||dS )a  
        Calculate confusion matrix from dataset and callback function.

        Args:
            dataset (DetectionDataset): Object detection dataset used for evaluation.
            callback (Callable[[np.ndarray], Detections]): Function that takes an image
                as input and returns Detections object.
            conf_threshold (float): Detection confidence threshold between `0` and `1`.
                Detections with lower confidence will be excluded.
            iou_threshold (float): Detection IoU threshold between `0` and `1`.
                Detections with lower IoU will be classified as `FP`.

        Returns:
            ConfusionMatrix: New instance of ConfusionMatrix.

        Example:
            ```python
            >>> import supervision as sv
            >>> from ultralytics import YOLO

            >>> dataset = sv.DetectionDataset.from_yolo(...)

            >>> model = YOLO(...)
            >>> def callback(image: np.ndarray) -> sv.Detections:
            ...     result = model(image)[0]
            ...     return sv.Detections.from_ultralytics(result)

            >>> confusion_matrix = sv.ConfusionMatrix.benchmark(
            ...     dataset = dataset,
            ...     callback = callback
            ... )

            >>> confusion_matrix.matrix
            array([
                [0., 0., 0., 0.],
                [0., 1., 0., 1.],
                [0., 1., 1., 0.],
                [1., 1., 0., 0.]
            ])
            ```
        r6   )imagesitemsr   r   r?   r.   )r:   rp   rq   r0   r1   r   r!   img_nameimgpredictions_batchtargets_batchr   r   r   	benchmarkW  s   
1

zConfusionMatrix.benchmarkNF   
   	save_pathOptional[str]titleOptional[List[str]]	normalizer   fig_sizeTuple[int, int]matplotlib.figure.Figurec                 C  s4  | j  }|rd}||ddd|  }tj||dk < tj|ddd\}}	|d	ur-|n| j}
|
d	uoAdt	|
  k o?d
k n  }|rS|
dg }|
dg }t	|}nd	}d	}t	|}|	j
|dd}|	jj||	d}|jjdt|d |d	u r|d}nd}|	jtd|||d |	jtd|||d tj|	 dddd |dk rdnd}|	jdd|d |dk rt|jd D ]5}t|jd D ]+}|||f }t|s|	j|||r|dn|ddd|d t| k rd!ndd" qq|r|	j|d#d$ |	d% |	d& |	d |r|j|d'| dd( |S ))at  
        Create confusion matrix plot and save it at selected location.

        Args:
            save_path (Optional[str]): Path to save the plot. If not provided,
                plot will be displayed.
            title (Optional[str]): Title of the plot.
            classes (Optional[List[str]]): List of classes to be displayed on the plot.
                If not provided, all classes will be displayed.
            normalize (bool): If True, normalize the confusion matrix.
            fig_size (Tuple[int, int]): Size of the plot.

        Returns:
            matplotlib.figure.Figure: Confusion matrix plot.
        g:0yE>r   r   rk   g{Gzt?Twhite)figsizetight_layout	facecolorNc   FNFPBlues)cmap)ax)vminvmaxrj   )labelsZ   rightdefault)rotationharotation_mode2   r|      both)r   which	labelsize   z.2fz.0fcenterr3   black)r   vacolor   )fontsize	PredictedTrue   )dpir   transparent) r,   copyrW   reshaper   nanpltsubplotsr.   r$   imshowfigurecolorbarmappableset_climnanmax
set_xticksarange
set_ytickssetpget_xticklabelstick_paramsranger(   isnantext	set_title
set_xlabel
set_ylabelset_facecolorsavefigget_facecolor)selfr}   r   r.   r   r   rN   epsfigr   class_namesuse_labels_for_ticksx_tick_labelsy_tick_labels	num_ticksimcbartick_intervalr   rf   rh   n_predsr   r   r   plot  sj   
$






zConfusionMatrix.plot)r2   r3   )r   r4   r!   r4   r.   r-   r0   r/   r1   r/   r   r+   )r   r    r!   r    r.   r-   r0   r/   r1   r/   r   r+   )r   r   r!   r   r@   rE   r0   r/   r1   r/   r   r   )rL   r   r   r   )
rp   r   rq   rr   r0   r/   r1   r/   r   r+   )NNNFrz   )r}   r~   r   r~   r.   r   r   r   r   r   r   r   )__name__
__module____qualname____doc____annotations__classmethodr?   r9   staticmethodrB   rS   ry   r   r   r   r   r   r+   H   s8   
 D\J@r+   T)frozenc                   @  s   e Zd ZU dZded< ded< ded< ded< ed'ddZed(ddZed)ddZe	d*ddZ
e	d+ddZe		d,d-d$d%Zd&S ).MeanAveragePrecisiona  
    Mean Average Precision for object detection tasks.

    Attributes:
        map50_95 (float): Mean Average Precision (mAP) calculated over IoU thresholds
            ranging from `0.50` to `0.95` with a step size of `0.05`.
        map50 (float): Mean Average Precision (mAP) calculated specifically at
            an IoU threshold of `0.50`.
        map75 (float): Mean Average Precision (mAP) calculated specifically at
            an IoU threshold of `0.75`.
        per_class_ap50_95 (np.ndarray): Average Precision (AP) values calculated over
            IoU thresholds ranging from `0.50` to `0.95` with a step size of `0.05`,
            provided for each individual class.
    r/   map50_95map50map75r   per_class_ap50_95r   r4   r!   r   c                 C  sN   g }g }t ||D ]\}}|t|dd |t|dd q	| j||dS )a  
        Calculate mean average precision based on predicted and ground-truth detections.

        Args:
            targets (List[Detections]): Detections objects from ground-truth.
            predictions (List[Detections]): Detections objects predicted by the model.
        Returns:
            MeanAveragePrecision: New instance of ConfusionMatrix.

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

            >>> targets = [
            ...     sv.Detections(...),
            ...     sv.Detections(...)
            ... ]

            >>> predictions = [
            ...     sv.Detections(...),
            ...     sv.Detections(...)
            ... ]

            >>> mean_average_precision = sv.MeanAveragePrecision.from_detections(
            ...     predictions=predictions,
            ...     targets=target,
            ... )

            >>> mean_average_precison.map50_95
            0.2899
            ```
        Tr5   Fr)   r7   )r:   r   r!   r;   r<   r=   r>   r   r   r   r?     s   &
z$MeanAveragePrecision.from_detectionsrp   r   rq   rr   c           	      C  sR   g g }}|j  D ]\}}||}|| |j| }|| q
| j||dS )a5  
        Calculate mean average precision from dataset and callback function.

        Args:
            dataset (DetectionDataset): Object detection dataset used for evaluation.
            callback (Callable[[np.ndarray], Detections]): Function that takes
                an image as input and returns Detections object.
        Returns:
            MeanAveragePrecision: New instance of MeanAveragePrecision.

        Example:
            ```python
            >>> import supervision as sv
            >>> from ultralytics import YOLO

            >>> dataset = sv.DetectionDataset.from_yolo(...)

            >>> model = YOLO(...)
            >>> def callback(image: np.ndarray) -> sv.Detections:
            ...     result = model(image)[0]
            ...     return sv.Detections.from_ultralytics(result)

            >>> mean_average_precision = sv.MeanAveragePrecision.benchmark(
            ...     dataset = dataset,
            ...     callback = callback
            ... )

            >>> mean_average_precision.map50_95
            0.433
            ```
        r)   )rs   rt   r   r   r?   )	r:   rp   rq   r   r!   ru   rv   rw   rx   r   r   r   ry   8  s   
%

zMeanAveragePrecision.benchmarkr    c              	   C  s@  t || tddd}g }t||D ]T\}}|jd dkr@|jd r?|tjd|jftdgtd|dddf R  q|jd rg| 	|||}|||ddd	f |dddf |dddf f q|rd
d t| D }| j
| }	|	dddf  }
|	ddd	f  }|	 }nd\}
}}g }	| ||
||	dS )a  
        Calculate Mean Average Precision based on predicted and ground-truth
            detections at different threshold.

        Args:
            predictions (List[np.ndarray]): Each element of the list describes
                a single image and has `shape = (M, 6)` where `M` is
                the number of detected objects. Each row is expected to be
                in `(x_min, y_min, x_max, y_max, class, conf)` format.
            targets (List[np.ndarray]): Each element of the list describes a single
                image and has `shape = (N, 5)` where `N` is the
                number of ground-truth objects. Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class)` format.
        Returns:
            MeanAveragePrecision: New instance of MeanAveragePrecision.

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

            >>> targets = (
            ...     [
            ...         array(
            ...             [
            ...                 [0.0, 0.0, 3.0, 3.0, 1],
            ...                 [2.0, 2.0, 5.0, 5.0, 1],
            ...                 [6.0, 1.0, 8.0, 3.0, 2],
            ...             ]
            ...         ),
            ...         array([1.0, 1.0, 2.0, 2.0, 2]),
            ...     ]
            ... )

            >>> predictions = [
            ...     array(
            ...         [
            ...             [0.0, 0.0, 3.0, 3.0, 1, 0.9],
            ...             [0.1, 0.1, 3.0, 3.0, 0, 0.9],
            ...             [6.0, 1.0, 8.0, 3.0, 1, 0.8],
            ...             [1.0, 6.0, 2.0, 7.0, 1, 0.8],
            ...         ]
            ...     ),
            ...     array([[1.0, 1.0, 2.0, 2.0, 2, 0.8]])
            ... ]

            >>> mean_average_precison = sv.MeanAveragePrecision.from_tensors(
            ...     predictions=predictions,
            ...     targets=targets,
            ... )

            >>> mean_average_precison.map50_95
            0.2899
            ```
        r3   gffffff?r|   r   rG   )rj   r   NrF   r#   c                 S  s   g | ]}t |d qS )r   )r   r   ).0rt   r   r   r   
<listcomp>  s    z5MeanAveragePrecision.from_tensors.<locals>.<listcomp>)r   r   r   )r   r   r   r   )r*   r   linspacer8   r(   r   rA   sizer   _match_detection_batch_average_precisions_per_classmean)r:   r   r!   iou_thresholdsstats	true_objspredicted_objsrL   concatenated_statsaverage_precisionsr   r   r   r   r   r   r9   h  sR   
<





z!MeanAveragePrecision.from_tensorsrecall	precisionc                 C  sl   t dg| dgf}t dg|dgf}t t jt |}t ddd}t |||}t ||}|S )a;  
        Compute the average precision using 101-point interpolation (COCO), given
            the recall and precision curves.

        Args:
            recall (np.ndarray): The recall curve.
            precision (np.ndarray): The precision curve.

        Returns:
            float: Average precision.
        g        g      ?r   r   e   )r   r   flipmaximum
accumulater   interptrapz)r   r   extended_recallextended_precisionmax_accumulated_precisioninterpolated_recall_levelsinterpolated_precisionaverage_precisionr   r   r   compute_average_precision  s   z.MeanAveragePrecision.compute_average_precisionr   c                 C  sr  | j d |j d }}tj||ftd}t|ddddf | ddddf }|ddddf | dddf k}t|D ]w\}}	t||	k|@ }
|
d j d rtj|
dd}||
 dddf }t||g}|
d j d dkr||dddf 	 ddd	  }|tj
|dddf d
dd  }|tj
|dddf d
dd  }d
||dddf t|f< q?|S )a3  
        Match predictions with target labels based on IoU levels.

        Args:
            predictions (np.ndarray): Batch prediction. Describes a single image and
                has `shape = (M, 6)` where `M` is the number of detected objects.
                Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class, conf)` format.
            targets (np.ndarray): Batch target labels. Describes a single image and
                has `shape = (N, 5)` where `N` is the number of ground-truth objects.
                Each row is expected to be in
                `(x_min, y_min, x_max, y_max, class)` format.
            iou_thresholds (np.ndarray): Array contains different IoU thresholds.

        Returns:
            np.ndarray: Matched prediction with target labels result.
        r   rG   NrF   r#   r   r   rj   rk   Trl   )r(   r   rA   r   r
   rV   whererR   hstackrn   ro   rU   rE   )r   r!   r   num_predictionsnum_iou_levelscorrectioucorrect_classrf   	iou_levelmatched_indicescombined_indices
iou_valuesrL   r   r   r   r     s"   *$"""z+MeanAveragePrecision._match_detection_batch缉ؗҜ<rL   prediction_confidenceprediction_class_idstrue_class_idsr   c                 C  s  t | }| | } || }t j|dd\}}|jd }t || jd f}	t|D ]U\}
}||k}||
 }| }|dksB|dkrCq*d| |  d}| | d}|||  }|||  }t| jd D ]}t	
|dd|f |dd|f |	|
|f< qfq*|	S )aW  
        Compute the average precision, given the recall and precision curves.
        Source: https://github.com/rafaelpadilla/Object-Detection-Metrics.

        Args:
            matches (np.ndarray): True positives.
            prediction_confidence (np.ndarray): Objectness value from 0-1.
            prediction_class_ids (np.ndarray): Predicted object classes.
            true_class_ids (np.ndarray): True object classes.
            eps (float, optional): Small value to prevent division by zero.

        Returns:
            np.ndarray: Average precision for different IoU levels.
        T)return_countsr   r   N)r   rn   ro   r(   rA   rV   rW   cumsumr   r   r   )rL   r  r  r  r   sorted_indicesunique_classesclass_countsr@   r   	class_idxr   is_class
total_truetotal_predictionfalse_positivestrue_positivesr   r   iou_level_idxr   r   r   r     s2   
z2MeanAveragePrecision._average_precisions_per_classN)r   r4   r!   r4   r   r   )rp   r   rq   rr   r   r   )r   r    r!   r    r   r   )r   r   r   r   r   r/   )r   r   r!   r   r   r   r   r   )r  )rL   r   r  r   r  r   r  r   r   r/   r   r   )r   r   r   r   r   r   r?   ry   r9   r   r   r   r   r   r   r   r   r     s&   
 1/k*r   )F)r   r	   r   r   r   r   )r   r    r!   r    )
__future__r   dataclassesr   typingr   r   r   r   
matplotlibmatplotlib.pyplotpyplotr   numpyr   supervision.dataset.corer   supervision.detection.corer	   supervision.detection.utilsr
   r   r*   r+   r   r   r   r   r   <module>   s&    
   *