Ë
    ëÓ^j)  ã                   ó    — d Z ddlmZ ddlmZ ddlZddlmZ  G d„ dee«      Z	de
ee
eef   f   d	e
ee
eef   f   fd
„ZdZdZ G d„ d«      Zy)zVOKS keypoint mAP metric backed by :class:`~rfdetr.evaluation.coco_eval.CocoEvaluator`.é    )ÚEnum)ÚAnyN)ÚCocoEvaluatorc                   ó    — e Zd ZdZdZdZdZdZy)ÚOKSKeyuf  Keys returned by :meth:`MetricKeypointOKS.compute`.

    Subclasses :class:`str` so enum members compare equal to their string values
    and can be used interchangeably as dict keys â€” ``stats[OKSKey.MAP]`` and
    ``stats["map"]`` both work.

    Examples:
        >>> OKSKey.MAP == "map"
        True
        >>> OKSKey.MAP_50.value
        'map_50'
    ÚmapÚmap_50Úmap_75ÚmarN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚMAPÚMAP_50ÚMAP_75ÚMAR© ó    úi/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/evaluation/keypoint_oks.pyr   r      s   „ ñð €CØ€FØ€FØ
�Cr   r   ÚpredictionsÚreturnc                 ó"  — | j                  «       D ����ci c]b  \  }}||j                  «       D ��ci c]@  \  }}|t        |t        j                  «      r|j	                  «       j                  «       n|“ŒB c}}“Œd c}}}}S c c}}w c c}}}}w )a  Return a copy of *predictions* with all tensors detached and moved to CPU.

    Prevents callers from inadvertently retaining CUDA memory or autograd graphs
    between :meth:`MetricKeypointOKS.update` calls.  Non-tensor values are kept as-is.

    Args:
        predictions: Per-image prediction dict mapping ``image_id`` to a dict of
            tensor-valued fields (``boxes``, ``scores``, ``labels``, ``keypoints``).

    Returns:
        New dict with the same structure; every :class:`torch.Tensor` value is
        replaced by its ``.detach().cpu()`` copy.

    Examples:
        >>> import torch
        >>> preds = {1: {"scores": torch.tensor([0.9], device="cpu"), "label": 2}}
        >>> sanitized = _sanitize_preds(preds)
        >>> sanitized[1]["label"]
        2
    )ÚitemsÚ
isinstanceÚtorchÚTensorÚdetachÚcpu)r   Úimage_idÚpredsÚkeyÚvalues        r   Ú_sanitize_predsr$   $   s‡   € ð2  +×0Ñ0Ó2÷	ñ ñ ˆH�eð 	Øfk×fqÑfqÓfs÷
ÙXbÐX[Ð]bˆC¬°E¼5¿<¹<Ô)H�—‘“×#Ñ#Ô%ÈeÑSó
ñ 	
õð ùó
ùõs   –B	
²ABÁ7B	
ÂB	
iô  )é
   c            	       ó’   — e Zd ZdZdefdedee   dz  deddfd„Z	e
defd„«       Zdd	„Zd
eeeeef   f   ddfd„Zdeeef   fd„Zy)ÚMetricKeypointOKSuÌ  OKS keypoint mAP metric backed by CocoEvaluator.

    Plain Python facade over :class:`~rfdetr.evaluation.coco_eval.CocoEvaluator`
    with a :meth:`reset` / :meth:`update` / :meth:`compute` interface that mirrors
    the torchmetrics API shape without subclassing it.

    DDP synchronisation is handled inside :meth:`compute` via
    :meth:`~rfdetr.evaluation.coco_eval.CocoEvaluator.synchronize_between_processes`,
    which uses the repo's pickle-based ``all_gather`` â€” avoiding the torchmetrics
    deadlock bugs #931 / #449 that affect variable-shape state tensors.

    Supports arbitrary keypoint counts and per-category OKS sigmas through the
    underlying :class:`~rfdetr.evaluation.coco_eval._GroupedKeypointCOCOeval`.

    When TorchMetrics ships production-quality arbitrary-keypoint support (tracked
    in upstream PR #3348), the internals of :meth:`compute` can delegate to
    ``MeanAveragePrecision(iou_type="keypoints", keypoint_format="xyv")`` without
    any change to callers.  Note: when migrating, ``"mar"`` will need remapping to
    ``"mar_<max_dets>"`` as TorchMetrics uses a suffixed key name.

    Args:
        coco_gt: Ground-truth COCO object.  Accepted types: :class:`faster_coco_eval.COCO`
            or any object with a ``.dataset`` dict and optional ``.label2cat`` mapping
            (the duck-typed surface required by :class:`~rfdetr.evaluation.coco_eval.CocoEvaluator`).
        keypoint_oks_sigmas: Per-keypoint OKS sigmas. When ``None``, falls back to
            COCO person sigmas for 17-keypoint datasets or a uniform 0.05 sigma for
            other counts.
        max_dets: Maximum detections per image passed to the underlying
            :class:`~rfdetr.evaluation.coco_eval.CocoEvaluator`.  Defaults to 500.

            Note:
                For keypoint evaluation the underlying COCO evaluator overrides
                ``maxDets`` to ``[20]`` regardless of this value â€” this parameter
                is forwarded but has no effect on keypoint evaluation.

    Examples:
        >>> from unittest.mock import MagicMock
        >>> metric = MetricKeypointOKS(MagicMock(), max_dets=100)
        >>> metric.has_updates
        False
        >>> metric.reset()  # idempotent on empty state
    NÚcoco_gtÚkeypoint_oks_sigmasÚmax_detsr   c                 ó<   — || _         || _        || _        g | _        y )N)Ú_coco_gtÚ_keypoint_oks_sigmasÚ	_max_detsÚ_batches)Úselfr(   r)   r*   s       r   Ú__init__zMetricKeypointOKS.__init__z   s#   € ð  ˆŒØ$7ˆÔ!Ø!ˆŒð :<ˆ�r   c                 ó,   — t        | j                  «      S )aß  Return whether any predictions have been accumulated since last reset.

        Returns:
            ``True`` if :meth:`update` has been called at least once since the
            last :meth:`reset`.

        Examples:
            >>> from unittest.mock import MagicMock
            >>> metric = MetricKeypointOKS(MagicMock())
            >>> metric.has_updates
            False
            >>> metric.update({1: {}})
            >>> metric.has_updates
            True
        )Úboolr/   ©r0   s    r   Úhas_updateszMetricKeypointOKS.has_updatesŽ   s   € ô" �D—M‘MÓ"Ð"r   c                 ó8   — | j                   j                  «        y)a!  Clear accumulated predictions.

        Examples:
            >>> from unittest.mock import MagicMock
            >>> metric = MetricKeypointOKS(MagicMock())
            >>> metric.update({1: {}})
            >>> metric.reset()
            >>> metric.has_updates
            False
        N)r/   Úclearr4   s    r   ÚresetzMetricKeypointOKS.reset¡   s   € ð 	�‰×ÑÕr   r   c                 óL   — | j                   j                  t        |«      «       y)u]  Accumulate per-batch predictions.

        Each call appends one batch; predictions are replayed in order inside
        :meth:`compute`.  Predictions for the same ``image_id`` across different
        calls are preserved as separate entries â€” no overwrite.

        Args:
            predictions: Mapping from ``image_id`` to a prediction dict with keys
                ``boxes`` (``[N, 4]`` xyxy pixel coords), ``scores`` (``[N]``),
                ``labels`` (``[N]`` int), and ``keypoints`` (``[N, K, 3]``
                x/y/confidence in pixel coords). Pass an empty dict for images
                with no predictions.

        Examples:
            >>> from unittest.mock import MagicMock
            >>> metric = MetricKeypointOKS(MagicMock())
            >>> metric.update({1: {}, 2: {}})
            >>> metric.has_updates
            True
        N)r/   Úappendr$   )r0   r   s     r   ÚupdatezMetricKeypointOKS.update®   s   € ð* 	�‰×Ñœ_¨[Ó9Õ:r   c                 óX  — t        | j                  dg| j                  | j                  d¬«      }| j                  D ]  }|j                  |«       Œ |j                  «        |j                  «        |j                  d   j                  }|j                  t        k(  sJ dt        › d|j                  › d�«       ‚t        j                  t        |d   «      t        j                  t        |d   «      t        j                   t        |d	   «      t        j"                  t        |d
   «      iS )a!  Run OKS keypoint evaluation and return metric dict.

        Constructs a fresh :class:`~rfdetr.evaluation.coco_eval.CocoEvaluator`,
        replays all accumulated per-batch predictions in order (matching the
        original per-batch ``CocoEvaluator.update()`` call pattern), synchronises
        across DDP ranks via
        :meth:`~rfdetr.evaluation.coco_eval.CocoEvaluator.synchronize_between_processes`,
        and accumulates COCO keypoint statistics.

        Returns:
            Dict with float values for keys :data:`METRIC_KEY_MAP` (mAP@50:95),
            :data:`METRIC_KEY_MAP_50` (AP@50), :data:`METRIC_KEY_MAP_75` (AP@75),
            and :data:`METRIC_KEY_MAR` (AR@50:95).  A value of ``-1.0`` indicates
            the statistic was not available (e.g. no predictions matched any ground-truth
            annotation).  Callers should filter ``value < 0`` before logging.

        Examples:
            >>> from unittest.mock import MagicMock, patch
            >>> import numpy as np
            >>> metric = MetricKeypointOKS(MagicMock(), max_dets=500)
            >>> fake_eval = MagicMock()
            >>> fake_eval.coco_eval = {
            ...     "keypoints": MagicMock(stats=np.array([0.5, 0.7, 0.4, -1, -1, 0.6, -1, -1, -1, -1]))
            ... }
            >>> with patch("rfdetr.evaluation.keypoint_oks.CocoEvaluator", return_value=fake_eval):
            ...     metric.update({1: {}})
            ...     result = metric.compute()
            >>> result["map"]
            0.5
            >>> result["map_50"]
            0.7
        Ú	keypointsF)r*   r)   Úlog_summaryz#Expected coco keypoint stats shape z, got uR   ; pycocotools _summarizeKps() contract violated â€” check faster_coco_eval versionr   é   é   é   )r   r,   r.   r-   r/   r;   Úsynchronize_between_processesÚ
accumulateÚ	coco_evalÚstatsÚshapeÚ_KPS_STATS_SHAPEr   r   Úfloatr   r   r   )r0   Ú	evaluatorÚbatchrE   s       r   ÚcomputezMetricKeypointOKS.computeÅ   s	  € ôB "Ø�M‰MØˆMØ—^‘^Ø $× 9Ñ 9Øô
ˆ	ð —]‘]ò 	$ˆEØ×Ñ˜UÕ#ð	$à×/Ñ/Ô1Ø×ÑÔØ×#Ñ# KÑ0×6Ñ6ˆØ�{‰{Ô.Ò.ð 	
Ø1Ô2BÐ1CÀ6È%Ï+É+Èð W_ð _ó	
Ð.ô
 �J‰Jœ˜e A™h›Ü�M‰Mœ5  q¡›?Ü�M‰Mœ5  q¡›?Ü�J‰Jœ˜e A™h›ð	
ð 	
r   )r   N)r   r   r   r   ÚDEFAULT_KEYPOINT_MAX_DETSr   ÚlistrH   Úintr1   Úpropertyr3   r5   r8   ÚdictÚstrr;   rK   r   r   r   r'   r'   N   sž   „ ñ)ð\ 37Ø1ñ	<àð<ð " %™[¨4Ñ/ð<ð ð	<ð
 
ó<ð( ð#˜Tò #ó ð#ó$ð; $ s¨D°°c°©NÐ':Ñ";ð ;Àó ;ð.6
˜˜c 5˜jÑ)ô 6
r   r'   )r   Úenumr   Útypingr   r   Úrfdetr.evaluation.coco_evalr   rQ   r   rP   rN   r$   rL   rG   r'   r   r   r   ú<module>rU      sz   ðñ ]å Ý ã å 5ôˆS�$ô ð(  c¨4°°S°©>Ð&9Ñ!:ð ¸tÀCÈÈcÐSVÈhÉÐDWÑ?Xó ðD  Ð ð
 Ð ÷m
ò m
r   