o
    WhG                     @   s   d dl mZ d dl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mZmZmZ d dlmZmZ G dd	 d	eZdS )
    )PathN)DetectionValidator)LOGGERops)check_requirements)	OKS_SIGMAPoseMetricsbox_ioukpt_iou)output_to_targetplot_imagesc                       s   e Zd ZdZd fdd	Z fddZdd Z fd	d
Z fddZ fddZ	dd Z
dddZdd Zdd Zdd Zdd Zdd Z  ZS )PoseValidatoraj  
    A class extending the DetectionValidator class for validation based on a pose model.

    This validator is specifically designed for pose estimation tasks, handling keypoints and implementing
    specialized metrics for pose evaluation.

    Attributes:
        sigma (np.ndarray): Sigma values for OKS calculation, either OKS_SIGMA or ones divided by number of keypoints.
        kpt_shape (List[int]): Shape of the keypoints, typically [17, 3] for COCO format.
        args (dict): Arguments for the validator including task set to "pose".
        metrics (PoseMetrics): Metrics object for pose evaluation.

    Methods:
        preprocess: Preprocesses batch data for pose validation.
        get_desc: Returns description of evaluation metrics.
        init_metrics: Initializes pose metrics for the model.
        _prepare_batch: Prepares a batch for processing.
        _prepare_pred: Prepares and scales predictions for evaluation.
        update_metrics: Updates metrics with new predictions.
        _process_batch: Processes batch to compute IoU between detections and ground truth.
        plot_val_samples: Plots validation samples with ground truth annotations.
        plot_predictions: Plots model predictions.
        save_one_txt: Saves detections to a text file.
        pred_to_json: Converts predictions to COCO JSON format.
        eval_json: Evaluates model using COCO JSON format.

    Examples:
        >>> from ultralytics.models.yolo.pose import PoseValidator
        >>> args = dict(model="yolo11n-pose.pt", data="coco8-pose.yaml")
        >>> validator = PoseValidator(args=args)
        >>> validator()
    Nc                    sj   t  ||||| d| _d| _d| j_t| jd| _t	| jj
tr1| jj
 dkr3td dS dS dS )a  
        Initialize a PoseValidator object for pose estimation validation.

        This validator is specifically designed for pose estimation tasks, handling keypoints and implementing
        specialized metrics for pose evaluation.

        Args:
            dataloader (torch.utils.data.DataLoader, optional): Dataloader to be used for validation.
            save_dir (Path | str, optional): Directory to save results.
            pbar (Any, optional): Progress bar for displaying progress.
            args (dict, optional): Arguments for the validator including task set to "pose".
            _callbacks (list, optional): List of callback functions to be executed during validation.

        Examples:
            >>> from ultralytics.models.yolo.pose import PoseValidator
            >>> args = dict(model="yolov8n-pose.pt", data="coco8-pose.yaml")
            >>> validator = PoseValidator(args=args)
            >>> validator()

        Notes:
            This class extends DetectionValidator with pose-specific functionality. It initializes with sigma values
            for OKS calculation and sets up PoseMetrics for evaluation. A warning is displayed when using Apple MPS
            due to a known bug with pose models.
        Npose)save_dirmpsz}Apple MPS known Pose bug. Recommend 'device=cpu' for Pose models. See https://github.com/ultralytics/ultralytics/issues/4031.)super__init__sigma	kpt_shapeargstaskr   r   metrics
isinstancedevicestrlowerr   warning)self
dataloaderr   pbarr   
_callbacks	__class__ T/var/www/vscode/kcb/lib/python3.10/site-packages/ultralytics/models/yolo/pose/val.pyr   1   s   zPoseValidator.__init__c                    s(   t  |}|d | j |d< |S )zSPreprocess batch by converting keypoints data to float and moving it to the device.	keypoints)r   
preprocesstor   float)r   batchr!   r#   r$   r&   U   s   zPoseValidator.preprocessc                 C   s   dd S )z:Return description of evaluation metrics in string format.z,%22s%11s%11s%11s%11s%11s%11s%11s%11s%11s%11s)ClassImages	InstanceszBox(PRmAP50	mAP50-95)zPose(Pr-   r.   r/   r#   r   r#   r#   r$   get_desc[   s   zPoseValidator.get_descc                    sb   t  | | jd | _| jddgk}| jd }|rtnt|| | _tg g g g g g d| _	dS )z2Initialize pose estimation metrics for YOLO model.r         r   )tp_ptpconfpred_cls
target_cls
target_imgN)
r   init_metricsdatar   r   nponesr   dictstats)r   modelis_posenkptr!   r#   r$   r:   k   s   
zPoseValidator.init_metricsc                    s   t  ||}|d |d |k }|d \}}| }|d  |9  < |d  |9  < tj|d ||d |d d}||d	< |S )
aS  
        Prepare a batch for processing by converting keypoints to float and scaling to original dimensions.

        Args:
            si (int): Batch index.
            batch (dict): Dictionary containing batch data with keys like 'keypoints', 'batch_idx', etc.

        Returns:
            pbatch (dict): Prepared batch with keypoints scaled to original image dimensions.

        Notes:
            This method extends the parent class's _prepare_batch method by adding keypoint processing.
            Keypoints are scaled from normalized coordinates to original image dimensions.
        r%   	batch_idximgsz).r   ).   	ori_shape	ratio_padrG   kpts)r   _prepare_batchcloner   scale_coords)r   sir)   pbatchrI   hwr!   r#   r$   rJ   t   s   zPoseValidator._prepare_batchc                    sd   t  ||}|d jd }|ddddf t||d}tj|d ||d |d d	 ||fS )
aC  
        Prepare and scale keypoints in predictions for pose processing.

        This method extends the parent class's _prepare_pred method to handle keypoint scaling. It first calls
        the parent method to get the basic prediction boxes, then extracts and scales the keypoint coordinates
        to match the original image dimensions.

        Args:
            pred (torch.Tensor): Raw prediction tensor from the model.
            pbatch (dict): Processed batch dictionary containing image information including:
                - imgsz: Image size used for inference
                - ori_shape: Original image shape
                - ratio_pad: Ratio and padding information for coordinate scaling

        Returns:
            predn (torch.Tensor): Processed prediction boxes scaled to original image dimensions.
        rI   rE   N   rD   rF   rG   rH   )r   _prepare_predshapeviewlenr   rL   )r   predrN   prednnk	pred_kptsr!   r#   r$   rS      s
   "zPoseValidator._prepare_predc                 C   s"  t |D ]	\}}|  jd7  _t|}ttjd| jdtjd| jdtj|| jtj| jdtj|| jtj| jdd}| 	||}|
d|
d}}	t|}
||d< | |d	< |dkr|
r| j D ]}| j| ||  qf| jjr| jjd
|	|d q| jjrd|d
d
df< | ||\}}|d
d
df |d< |d
d
df |d< |
r| ||	||d< | ||	|||d |d< | jjr| j||	| | j D ]}| j| ||  q| jjr| ||d |  | jjr| ||| jj|d | jd t|d | j d  qd
S )a  
        Update metrics with new predictions and ground truth data.

        This method processes each prediction, compares it with ground truth, and updates various statistics
        for performance evaluation.

        Args:
            preds (List[torch.Tensor]): List of prediction tensors from the model.
            batch (dict): Batch data containing images and ground truth annotations.
        rE   r   )r   )dtyper   )r6   r7   r5   r4   clsbboxr8   r9   N)
detections	gt_bboxesgt_cls      r6   r7   r5   rI   r4   im_filerF   labelsz.txt)	enumerateseenrV   r>   torchzerosr   niouboolrJ   popuniquer?   keysappendr   plotsconfusion_matrixprocess_batch
single_clsrS   _process_batch	save_jsonpred_to_jsonsave_txtsave_one_txt	save_confr   r   stem)r   predsr)   rM   rW   nprstatrN   r\   r]   nlkrX   rZ   r#   r#   r$   update_metrics   sZ   
 zPoseValidator.update_metricsc                 C   s|   |dur$|dur$t |ddddf dd }t||| j|d}nt||ddddf }| |dddf ||S )a  
        Return correct prediction matrix by computing Intersection over Union (IoU) between detections and ground truth.

        Args:
            detections (torch.Tensor): Tensor with shape (N, 6) representing detection boxes and scores, where each
                detection is of the format (x1, y1, x2, y2, conf, class).
            gt_bboxes (torch.Tensor): Tensor with shape (M, 4) representing ground truth bounding boxes, where each
                box is of the format (x1, y1, x2, y2).
            gt_cls (torch.Tensor): Tensor with shape (M,) representing ground truth class indices.
            pred_kpts (torch.Tensor | None): Optional tensor with shape (N, 51) representing predicted keypoints, where
                51 corresponds to 17 keypoints each having 3 values.
            gt_kpts (torch.Tensor | None): Optional tensor with shape (N, 51) representing ground truth keypoints.

        Returns:
            (torch.Tensor): A tensor with shape (N, 10) representing the correct prediction matrix for 10 IoU levels,
                where N is the number of detections.

        Notes:
            `0.53` scale factor used in area computation is referenced from
            https://github.com/jin-s13/xtcocoapi/blob/master/xtcocotools/cocoeval.py#L384.
        N   rE   g(\?)r   arearb   ra   )r   	xyxy2xywhprodr
   r   r	   match_predictions)r   r^   r_   r`   rZ   gt_kptsr   iour#   r#   r$   rs      s
   $zPoseValidator._process_batchc                 C   sN   t |d |d |d d|d |d |d | jd| d	 | j| jd
	 dS )am  
        Plot and save validation set samples with ground truth bounding boxes and keypoints.

        Args:
            batch (dict): Dictionary containing batch data with keys:
                - img (torch.Tensor): Batch of images
                - batch_idx (torch.Tensor): Batch indices for each image
                - cls (torch.Tensor): Class labels
                - bboxes (torch.Tensor): Bounding box coordinates
                - keypoints (torch.Tensor): Keypoint coordinates
                - im_file (list): List of image file paths
            ni (int): Batch index used for naming the output file
        imgrC   r\   rR   bboxesr%   rc   	val_batchz_labels.jpgrI   pathsfnamenameson_plotN)r   squeezer   r   r   )r   r)   nir#   r#   r$   plot_val_samples  s   
zPoseValidator.plot_val_samplesc                    sb   t  fdd|D d}t|d gt| jjdR ||d  jd| d  j jd	 d
S )a  
        Plot and save model predictions with bounding boxes and keypoints.

        Args:
            batch (dict): Dictionary containing batch data including images, file paths, and other metadata.
            preds (List[torch.Tensor]): List of prediction tensors from the model, each containing bounding boxes,
                confidence scores, class predictions, and keypoints.
            ni (int): Batch index used for naming the output file.

        The function extracts keypoints from predictions, converts predictions to target format, and plots them
        on the input images. The resulting visualization is saved to the specified save directory.
        c                    s0   g | ]}|d d dd f j dg jR  qS )NrQ   rR   )rU   r   ).0pr0   r#   r$   
<listcomp>)  s   0 z2PoseValidator.plot_predictions.<locals>.<listcomp>r   r   )max_detrc   r   z	_pred.jpgr   N)	rg   catr   r   r   r   r   r   r   )r   r)   rz   r   rZ   r#   r0   r$   plot_predictions  s   zPoseValidator.plot_predictionsc                 C   sV   ddl m} |tj|d |d ftjdd| j|ddddf |dj||d dS )	a  
        Save YOLO pose detections to a text file in normalized coordinates.

        Args:
            predn (torch.Tensor): Prediction boxes and scores with shape (N, 6) for (x1, y1, x2, y2, conf, cls).
            pred_kpts (torch.Tensor): Predicted keypoints with shape (N, K, D) where K is the number of keypoints
                and D is the dimension (typically 3 for x, y, visibility).
            save_conf (bool): Whether to save confidence scores.
            shape (tuple): Original image shape (height, width).
            file (Path): Output file path to save detections.

        Notes:
            The output format is: class_id x_center y_center width height confidence keypoints where keypoints are
            normalized (x, y, visibility) values for each point.
        r   )ResultsrE   )r[   NrQ   )pathr   boxesr%   )rx   )ultralytics.engine.resultsr   r<   rh   uint8r   rv   )r   rX   rZ   rx   rT   filer   r#   r#   r$   rw   4  s   
zPoseValidator.save_one_txtc              
   C   s   t |j}| rt|n|}t|ddddf }|ddddf  |ddddf d 8  < t| | D ]%\}}| j	|| j
t|d  dd |D |dd t|d dd q=dS )	a  
        Convert YOLO predictions to COCO JSON format.

        This method takes prediction tensors and a filename, converts the bounding boxes from YOLO format
        to COCO format, and appends the results to the internal JSON dictionary (self.jdict).

        Args:
            predn (torch.Tensor): Prediction tensor containing bounding boxes, confidence scores, class IDs,
                and keypoints, with shape (N, 6+K) where N is the number of predictions and K is the flattened
                keypoints dimension.
            filename (str | Path): Path to the image file for which predictions are being processed.

        Notes:
            The method extracts the image ID from the filename stem (either as an integer if numeric, or as a string),
            converts bounding boxes from xyxy to xywh format, and adjusts coordinates from center to top-left corner
            before saving to the JSON dictionary.
        Nrb   r   ra   c                 S   s   g | ]}t |d qS )r3   )roundr   xr#   r#   r$   r   i  s    z.PoseValidator.pred_to_json.<locals>.<listcomp>rQ   )image_idcategory_idr]   r%   score)r   ry   	isnumericintr   r   ziptolistjdictrn   	class_mapr   )r   rX   filenamery   r   boxr   br#   r#   r$   ru   N  s   
0
zPoseValidator.pred_to_jsonc              
   C   s~  | j jr| jrt| jr| jd d }| jd }td| d| d z}t	d dd	l
m} dd
lm} ||fD ]}| sFJ | dq9|t|}|t|}t|||d|||dgD ]<\}	}
| jrudd | jjjD |
j_|
  |
  |
  |	d d }|
jdd \|| jj|d  < || jj| < qbW |S  ty } ztd|  W Y d}~|S d}~ww |S )z7Evaluate object detection model using COCO JSON format.r   z)annotations/person_keypoints_val2017.jsonzpredictions.jsonz"
Evaluating pycocotools mAP using z and z...zpycocotools>=2.0.6r   )COCO)COCOevalz file not foundr]   r%   c                 S   s   g | ]	}t t|jqS r#   )r   r   ry   r   r#   r#   r$   r     s    z+PoseValidator.eval_json.<locals>.<listcomp>rb   r   NrE   zpycocotools unable to run: )r   rt   is_cocorV   r   r;   r   r   infor   pycocotools.cocor   pycocotools.cocoevalr   is_filer   loadResre   r   datasetim_filesparamsimgIdsevaluate
accumulate	summarizer?   r   rm   	Exceptionr   )r   r?   	anno_json	pred_jsonr   r   r   annorW   ievalidxer#   r#   r$   	eval_jsono  s<   
$&zPoseValidator.eval_json)NNNNN)NN)__name__
__module____qualname____doc__r   r&   r1   r:   rJ   rS   r   rs   r   r   rw   ru   r   __classcell__r#   r#   r!   r$   r      s    !$	
>!r   )pathlibr   numpyr<   rg   ultralytics.models.yolo.detectr   ultralytics.utilsr   r   ultralytics.utils.checksr   ultralytics.utils.metricsr   r   r	   r
   ultralytics.utils.plottingr   r   r   r#   r#   r#   r$   <module>   s   