
    ^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	  He 	fkfqfqfs
XbX[]bCE5<<)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    " DMM""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 "MMM^^ $ 9 9
	 ]] 	$EU#	$//1##K066{{.. 	
12B1C6%++ W_ _	
.
 JJeAhMM5q?MM5q?JJeAh	
 	
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 # #$;$sDcN':"; ; ;.6
c5j) 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$ (c4S>&9!: tCcSVhDW?X D   
  m
 m
r   