Ë
    ëÓ^j¨²  ã                  óP  — d 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l
ZddlZddl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 ddlmZ  e«       Z G d„ de«      Zh d£Z eh d£«      Z dd„Z!dd„Z" ed¬«      dd„«       Z#dd„Z$ G d„ d«      Z%y# e$ r dZY Œ€w xY w)z7Transforms and data augmentation for both image + bbox.é    )ÚannotationsN)ÚSequence)Ú	lru_cache)ÚAny)ÚImage)ÚTensor)Ú	Normalize)Ú#filter_keypoint_hflip_augmentations)Úbox_xyxy_to_cxcywh)Ú
get_loggerc                  ó.   — e Zd Z	 	 d	 	 	 	 	 dd„Zddd„Zy)r	   c                ó&   — t        ||«      | _        y ©N)Ú_TVNormalizeÚ
_normalize)ÚselfÚmeanÚstds      úe/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/datasets/transforms.pyÚ__init__zNormalize.__init__+   s   € ô
 ' t¨SÓ1ˆ�ó    Nc                ój  — | j                  |«      }|€|dfS |j                  «       }|j                  dd \  }}d|v rA|d   }t        |«      }|t	        j
                  ||||gt        j                  ¬«      z  }||d<   d|v r.|d   j                  «       }|d   |z  |d<   |d   |z  |d<   ||d<   ||fS )a—  Normalize image and convert target coordinates to relative format.

        Applies ImageNet-style channel normalization to the image, then converts
        bounding boxes from absolute xyxy pixel coordinates to normalized cxcywh
        format (divided by ``[w, h, w, h]``) and scales keypoint x/y by image
        width/height respectively.

        Args:
            image: CHW float tensor to normalize.
            target: Optional dict with keys ``"boxes"`` (xyxy pixel coords,
                shape ``[N, 4]``) and/or ``"keypoints"`` (shape ``[N, K, 3]``
                where the third channel is visibility). Mutated copy returned;
                original is not modified.

        Returns:
            Tuple of ``(normalized_image, target)`` where ``target`` has boxes
            in normalized cxcywh format and keypoints scaled to ``[0, 1]``, or
            ``(normalized_image, None)`` when ``target`` is ``None``.

        Examples:
            >>> import torch
            >>> normalize = Normalize()
            >>> img = torch.zeros(3, 64, 64)
            >>> out_img, out_tgt = normalize(img, None)
            >>> out_tgt is None
            True
        NéþÿÿÿÚboxes©ÚdtypeÚ	keypoints).r   ).é   )r   ÚcopyÚshaper   ÚtorchÚtensorÚfloat32Úclone)r   ÚimageÚtargetÚhÚwr   r   s          r   Ú__call__zNormalize.__call__2   sÜ   € ð8 —‘ Ó&ˆØˆ>Ø˜$�;ÐØ—‘“ˆØ�{‰{˜2˜3Ð‰ˆˆ1Ø�fÑØ˜7‘OˆEÜ& uÓ-ˆEØœEŸL™L¨!¨Q°°1¨¼U¿]¹]ÔKÑKˆEØ#ˆF�7‰OØ˜&Ñ Ø˜{Ñ+×1Ñ1Ó3ˆIØ )¨&Ñ 1°AÑ 5ˆI�fÑØ )¨&Ñ 1°AÑ 5ˆI�fÑØ"+ˆF�;ÑØ�fˆ}Ðr   ))g
×£p=
ß?gÉv¾Ÿ/Ý?g–C‹lçûÙ?)gZd;ßOÍ?gyé&1¬Ì?gÍÌÌÌÌÌÌ?)r   útuple[float, ...]r   r*   ÚreturnÚNoner   )r%   r   r&   údict[str, Any] | Noner+   z$tuple[Tensor, dict[str, Any] | None])Ú__name__Ú
__module__Ú__qualname__r   r)   © r   r   r	   r	   *   s0   „ ð #8Ø!6ð2àð2ð ð2ð 
ó	2õ+r   r	   >&   ÚD4ÚPadÚCropÚFlipÚAffineÚResizeÚRotateÚ	DownscaleÚ	TransposeÚ
CenterCropÚ
CropAndPadÚ
RandomCropÚ
SafeRotateÚPadIfNeededÚPerspectiveÚRandomScaleÚVerticalFlipÚGridDistortionÚHorizontalFlipÚLongestMaxSizeÚRandomRotate90ÚSquareSymmetryÚPiecewiseAffineÚRandomSizedCropÚSmallestMaxSizeÚThinPlateSplineÚElasticTransformÚShiftScaleRotateÚGridElasticDeformÚOpticalDistortionÚRandomGridShuffleÚRandomResizedCropÚBBoxSafeRandomCropÚRandomCropNearBBoxÚRandomCropFromBordersÚRandomSizedBBoxSafeCropÚAtLeastOneBBoxRandomCropÚCropNonEmptyMaskIfExists>   ÚOneOfÚSomeOfÚ
Sequentialc                óŒ   — t        | «      j                  t        v ryt        | d«      rt	        d„ | j
                  D «       «      S y)a÷  Return True if transform (or any nested transform) affects spatial coordinates.

    For container transforms such as ``A.OneOf`` or ``A.Sequential``, returns ``True`` when *any* nested transform is
    geometric so that bounding-box handling is enabled for the whole container.

    Args:
        transform: Albumentations transform to inspect.

    Returns:
        ``True`` if the transform modifies spatial layout; ``False`` otherwise.

    Examples:
        >>> from albumentations import GaussianBlur, HorizontalFlip, OneOf
        >>> _is_geometric_transform(HorizontalFlip())
        True
        >>> _is_geometric_transform(GaussianBlur())
        False
        >>> _is_geometric_transform(OneOf([HorizontalFlip(), GaussianBlur()]))
        True
    TÚ
transformsc              3  ó2   K  — | ]  }t        |«      –— Œ y ­wr   )Ú_is_geometric_transform)Ú.0Úts     r   ú	<genexpr>z*_is_geometric_transform.<locals>.<genexpr>±   s   è ø€ ÒL°!Ô*¨1×-ÑLùs   ‚F)Útyper.   ÚGEOMETRIC_TRANSFORMSÚhasattrÚanyr\   )Ú	transforms    r   r^   r^   ˜   s>   € ô* ˆIƒ×ÑÔ#7Ñ7Øäˆy˜,Ô'ÜÑL°y×7KÑ7KÔLÓLÐLØr   c           	     ó.  — t         €t        d«      ‚| t        v �r¶|j                  dg «      }t	        |t
        «      s$t        d| › dt        |«      j                  › �«      ‚g }|D ]¢  }t	        |t        «      rt        |«      dk7  rt        d|›�«      ‚t        t        |j                  «       «      «      \  }}t	        |t        «      s$t        d|› d	t        |«      j                  › �«      ‚|j                  t        ||«      «       Œ¤ | d
k(  r;|st        d«      ‚|j                  «       D ��ci c]  \  }}|dvsŒ||“Œ }	}}d|	d<   n\| dk(  r.|j                  «       D ��ci c]  \  }}|dvsŒ||“Œ }	}}d|	d<   n)|j                  «       D ��ci c]  \  }}|dk7  sŒ||“Œ }	}}t!        t         | d«      }
|
€t        d| ›�«      ‚ |
dd|i|	¤ŽS t!        t         | d«      }|€t        d| ›�«      ‚ |di t#        | ||«      ¤ŽS c c}}w c c}}w c c}}w )aä  Build a single Albumentations transform from its name and parameter dict.

    Handles container transforms (``OneOf``, ``SomeOf``, ``Sequential``) by recursively building the nested
    ``transforms`` list.  Leaf transforms are instantiated directly from the ``albumentations`` namespace.

    Both ``OneOf`` and ``Sequential`` always fire (``p=1.0`` is forced, ignoring any user-supplied ``p``).  For
    ``OneOf``, which child is applied is determined by the children's own ``p`` values; at least one nested transform is
    required.  ``Sequential`` runs all transforms in order.

    Args:
        name: Transform name (e.g. ``"HorizontalFlip"``, ``"OneOf"``).
        params: Parameter dictionary for the transform.  For container transforms
            the dict must contain a ``"transforms"`` key whose value is a list of single-key dicts ``{name: params}``.

    Returns:
        Instantiated Albumentations transform.

    Raises:
        ImportError: If Albumentations is not installed.
        ValueError: If ``name`` is unknown or ``params`` is malformed.

    Examples:
        >>> from albumentations import HorizontalFlip, OneOf
        >>> t = _build_albu_transform("HorizontalFlip", {"p": 0.5})
        >>> isinstance(t, HorizontalFlip)
        True
        >>> container = _build_albu_transform(
        ...     "OneOf",
        ...     {"transforms": [{"HorizontalFlip": {"p": 1.0}}, {"VerticalFlip": {"p": 1.0}}]},
        ... )
        >>> isinstance(container, OneOf)
        True
    Nú—Albumentations is required to build RF-DETR dataset transforms. Install the project dependencies with `uv sync --all-groups` or install albumentations.r\   ú'z!.transforms' must be a list, got r   z;Each nested transform entry must be a single-key dict, got z!Parameters for nested transform 'z' must be a dict, got rX   z''OneOf' requires at least one transform)r\   Úpg      ð?rj   rZ   z"Unknown Albumentations container: z"Unknown Albumentations transform: r1   )ÚalbÚImportErrorÚALBUMENTATIONS_CONTAINERSÚgetÚ
isinstanceÚlistÚ
ValueErrorrb   r.   ÚdictÚlenÚnextÚiterÚitemsÚappendÚ_build_albu_transformÚgetattrÚ_normalize_albu_params)ÚnameÚparamsÚ
raw_nestedÚnested_transformsÚentryÚnested_nameÚnested_paramsÚkÚvÚother_paramsÚcontainer_clsÚaug_clss               r   rx   rx   µ   sL  € ôD €{Üðfó
ð 	
ð
 Ô(Ò(Ø—Z‘Z ¨bÓ1ˆ
Ü˜*¤dÔ+Ü˜q  Ð&GÌÈZÓHX×HaÑHaÐGbÐcÓdÐdØ68ÐØò 		XˆEÜ˜e¤TÔ*¬c°%«j¸AªoÜ Ð#^Ð_dÐ^gÐ!hÓiÐiÜ)-¬d°5·;±;³=Ó.AÓ)BÑ&ˆK˜Ü˜m¬TÔ2Ü Ø7¸°}ð EÜ Ó.×7Ñ7Ð8ð:óð ð ×$Ñ$Ô%:¸;ÈÓ%VÕWð		Xð �7Š?Ù$Ü Ð!JÓKÐKØ-3¯\©\«^×\¡T Q¨¸qÐH[Ò?[˜A˜q™DÐ\ˆLÑ\Ø #ˆL˜ÒØ�\Ò!Ø-3¯\©\«^×\¡T Q¨¸qÐH[Ò?[˜A˜q™DÐ\ˆLÑ\Ø #ˆL˜Òà-3¯\©\«^×Q¡T Q¨¸qÀLÓ?P˜A˜q™DÐQˆLÑQä¤ T¨4Ó0ˆØÐ ÜÐAÀ$ÀÐJÓKÐKÙÑJÐ(9ÐJ¸\ÑJÐJä”c˜4 Ó&€GØ€ÜÐ=¸d¸XÐFÓGÐGÙÑCÔ+¨D°&¸'ÓBÑCÐCùó! ]ùó ]ùó Rs$   Ä/HÄ<HÅ"HÅ/HÆHÆH)Úmaxsizec                ó\   — t        j                  | j                  «      }d|j                  v S )a  Return whether ``RandomSizedCrop`` expects a ``size`` keyword.

    The Albumentations 2.x API changed ``RandomSizedCrop`` from separate ``height``/``width`` parameters to a single
    ``size=(height, width)`` parameter. This helper caches the signature check per class so repeated transform
    construction during dataset setup does not repeat introspection.

    Args:
        aug_cls: Albumentations transform class to inspect.

    Returns:
        ``True`` when the class accepts a ``size`` keyword argument; otherwise ``False``.
    Úsize)ÚinspectÚ	signaturer   Ú
parameters)r†   r‹   s     r   Ú"_random_sized_crop_uses_size_paramr�     s+   € ô ×!Ñ! '×"2Ñ"2Ó3€IØ�Y×)Ñ)Ð)Ð)r   c                ó(  — t        |«      }| dk7  r|S t        |«      }|r�d|v }d|v }d|v }|r&|j                  dd«       |j                  dd«       |S |r-|r+|j                  d«      }|j                  d«      }	||	f|d<   |S ||k7  r|r|sdnd}
t        d|
› d�«      ‚|S |sod|v rk|j	                  d«      }t        |t        «      rJt        |«      dk(  r<|j                  d|d	   «       |j                  d|d
   «       |j                  dd«       |S )aœ  Normalize transform params across Albumentations API variations.

    Currently this adapts ``RandomSizedCrop`` arguments so a config using ``height``/``width`` works on Albumentations
    2.x and a config using ``size=(height, width)`` still works on Albumentations 1.x.

    Args:
        name: Albumentations transform name.
        params: Raw transform parameter mapping from config.
        aug_cls: Albumentations transform class that will be instantiated.

    Returns:
        A normalized copy of ``params`` suitable for the installed Albumentations version.

    Examples:
        >>> class CropV2:
        ...     def __init__(self, *, size, min_max_height): ...
        >>> _normalize_albu_params(
        ...     "RandomSizedCrop",
        ...     {"min_max_height": [384, 600], "height": 640, "width": 640},
        ...     CropV2,
        ... )
        {'min_max_height': [384, 600], 'size': (640, 640)}
    rI   r‰   ÚheightÚwidthNz˜RandomSizedCrop for the installed Albumentations version expects 'size=(height, width)'. Received only one of 'height'/'width' without 'size' (missing 'z').é   r   r   )	rr   r�   Úpoprq   rn   ro   r   rs   Ú
setdefault)r{   r|   r†   Únormalized_paramsÚ	uses_sizeÚhas_sizeÚ
has_heightÚ	has_widthr�   r�   Úmissingr‰   s               r   rz   rz     s^  € ô0 ˜V›ÐØÐ Ò Ø Ð ä2°7Ó;€Iáð Ð.Ð.ˆØÐ!2Ð2ˆ
ØÐ0Ð0ˆ	áð ×!Ñ! (¨DÔ1Ø×!Ñ! '¨4Ô0Ø$Ð$á™)Ø&×*Ñ*¨8Ó4ˆFØ%×)Ñ)¨'Ó2ˆEØ)/°¨Ð˜fÑ%Ø$Ð$à˜Ò"ñ ",±I‘gÀ8ˆGÜð,à,3¨9°Cð9óð ð !Ð ñ ˜Ð#4Ñ4Ø ×$Ñ$ VÓ,ˆÜ�dœHÔ%¬#¨d«)°qª.Ø×(Ñ(¨°4¸±7Ô;Ø×(Ñ(¨°$°q±'Ô:ð ×!Ñ! &¨$Ô/àÐr   c                  ó  — e Zd ZdZddd„Zdd„Zedd„«       Zedd„«       Ze	 	 	 	 	 	 dd„«       Z	edd„«       Z
e	 	 d	 	 	 	 	 	 	 	 	 	 	 dd	„«       Zedd
„«       Zedd„«       Z	 	 	 	 	 	 	 	 dd„Z	 	 	 	 	 	 dd„Ze	 d	 	 	 	 	 dd„«       Zy)ÚAlbumentationsWrapperaæ  Wrapper to apply Albumentations transforms to (image, target) tuples.

    This wrapper integrates Albumentations transforms with RF-DETR's data pipeline, automatically handling bounding box
    and segmentation mask transformations for geometric augmentations while preserving the (image, target) tuple format.

    The wrapper automatically detects transform types:
    - **Geometric transforms** (flips, rotations, crops): Bounding boxes and instance
      masks are transformed along with the image to maintain correct object localization.
    - **Pixel-level transforms** (blur, color adjustments, noise): Bounding boxes and
      masks remain unchanged as only pixel values are modified.

    Detection checks the transform class name against ``GEOMETRIC_TRANSFORMS`` and recursively inspects nested container
    transforms (for example ``OneOf`` and ``Sequential``). For geometric transforms, bbox_params are automatically
    configured to handle coordinate transformations, clip boxes to image boundaries, and remove invalid boxes.

    Args:
        transform: Albumentations transform to apply (e.g., alb.HorizontalFlip, alb.GaussianBlur).
        keypoint_flip_pairs: Joint index pairs for left/right swapping after a horizontal flip.
            ``None`` (default) means a detection pipeline -- no keypoint handling.
            An empty list ``[]`` marks a keypoint pipeline without semantic flip
            pairs, so horizontal-flip transforms should have been stripped from
            config before this point.

    Examples:
        >>> from albumentations import GaussianBlur, HorizontalFlip
        >>> # Geometric transform - automatically transforms boxes
        >>> wrapper = AlbumentationsWrapper(HorizontalFlip(p=1.0))
        >>> image = Image.new("RGB", (300, 400))
        >>> target = {"boxes": torch.tensor([[10, 20, 100, 200]]), "labels": torch.tensor([1])}
        >>> aug_image, aug_target = wrapper(image, target)

        >>> # Pixel-level transform - automatically preserves boxes
        >>> wrapper = AlbumentationsWrapper(GaussianBlur(p=1.0))
        >>> aug_image, aug_target = wrapper(image, target)

    Note:
        For custom geometric transforms, add the transform class name to the GEOMETRIC_TRANSFORMS set at module level.
    Nc           	     óü  — t        |«      | _        t        |xs g «      | _        | j                  r±t	        | j                  «      }|r%t        t        d«      st        j                  d«       |r t        t        d«      rt        j                  nt        j                  } ||gt        j                  dddgdd¬«      t        j                  d	g d
¢d¬«      ¬«      | _        y t        j                  |g«      | _        y )NÚReplayComposez€albumentations.ReplayCompose not available; horizontal-flip keypoint slot swapping is disabled. Upgrade albumentations to >=1.3.Ú
pascal_vocÚcategory_idsÚidxsg        T)ÚformatÚlabel_fieldsÚmin_visibilityÚclipÚxy)Úkeypoint_instance_idsÚkeypoint_point_idsÚkeypoint_visibilityF)r¡   r¢   Úremove_invisible)Úbbox_paramsÚkeypoint_params)r^   Ú_is_geometricrp   Ú_keypoint_flip_pairsÚboolrd   rk   ÚloggerÚwarningr�   ÚComposeÚ
BboxParamsÚKeypointParamsrf   )r   rf   Úkeypoint_flip_pairsÚneeds_replayÚcompose_clss        r   r   zAlbumentationsWrapper.__init__’  s×   € ä4°YÓ?ˆÔÜ$(Ð)<Ò)BÀÓ$CˆÔ!à×Òô   × 9Ñ 9Ó:ˆLÙ¤G¬C°Ô$AÜ—‘ðRôñ 1=ÄÌÈoÔA^œ#×+Ò+Ôeh×epÑepˆKÙ(Ø�ÜŸN™NØ'Ø"0°&Ð!9Ø#&Øô	ô !$× 2Ñ 2ØÚ!gØ%*ô!ôˆD�Nô" !Ÿ[™[¨)¨Ó5ˆD�Nr   c                ó®  — d}t        | j                  t        j                  «      r:| j                  j                  D ]   }t        |t        j
                  «      sŒ|} n2 n0t        | j                  t        j
                  «      r| j                  }|€t        j                  | «      S | j                  rdnd}| j                  j                  › d|› d|› d�S )z—Return a readable string representation of the wrapper.

        Returns:
            Representation including the wrapped transform and type.
        NÚ	geometriczpixel-levelz(transform=z, type=ú))ro   rf   rk   r±   r\   ÚBasicTransformÚobjectÚ__repr__r¬   Ú	__class__r.   )r   rf   Ú	candidateÚtransform_types       r   r¼   zAlbumentationsWrapper.__repr__´  sµ   € ð ˆ	Ü�d—n‘n¤c§k¡kÔ2Ø!Ÿ^™^×6Ñ6ò �	Ü˜i¬×);Ñ);Õ<Ø )�IÙñô ˜Ÿ™¬×(:Ñ(:Ô;ØŸ™ˆIàÐÜ—?‘? 4Ó(Ð(à(,×(:Ò(:™ÀˆØ—.‘.×)Ñ)Ð*¨+°i°[ÀÈÐGWÐWXÐYÐYr   c                ó  — t        j                  | «      r| j                  «       j                  «       nt	        j
                  | «      }t        |j                  «      dk7  s|j                  d   dk7  rt        d|j                  › �«      ‚|S )zÞConvert boxes to numpy array and validate shape.

        >>> import torch
        >>> boxes = torch.tensor([[10.0, 20.0, 30.0, 40.0]])
        >>> AlbumentationsWrapper._boxes_to_numpy(boxes).shape
        (1, 4)
        r‘   r   é   z"boxes must have shape (N, 4), got ©	r!   Ú	is_tensorÚcpuÚnumpyÚnpÚarrayrs   r    rq   )r   Úboxes_nps     r   Ú_boxes_to_numpyz%AlbumentationsWrapper._boxes_to_numpyÉ  sk   € ô +0¯/©/¸%Ô*@�5—9‘9“;×$Ñ$Ô&ÄbÇhÁhÈuÃoˆÜˆx�~‰~Ó !Ò# x§~¡~°aÑ'8¸AÒ'=ÜÐAÀ(Ç.Á.ÐAQÐRÓSÐSØˆr   c                óz  — t        j                  | «      r| j                  «       j                  «       nt	        j
                  | «      }t        |j                  «      dk7  s|j                  d   dk7  rt        d|j                  › �«      ‚|j                  d   |k7  rt        d|› d|j                  d   › �«      ‚|S )zïConvert keypoints to numpy array and validate shape.

        >>> import torch
        >>> keypoints = torch.tensor([[[10.0, 20.0, 2.0]]])
        >>> AlbumentationsWrapper._keypoints_to_numpy(keypoints, 1).shape
        (1, 1, 3)
        é   r‘   z)keypoints must have shape (N, K, 3), got r   z6keypoints first dimension must match number of boxes (z), got rÂ   )r   Ú	num_boxesÚkeypoints_nps      r   Ú_keypoints_to_numpyz)AlbumentationsWrapper._keypoints_to_numpy×  s¸   € ô 38·/±/À)Ô2L�y—}‘}“×,Ñ,Ô.ÔRT×RZÑRZÐ[dÓReˆÜˆ|×!Ñ!Ó" aÒ'¨<×+=Ñ+=¸aÑ+@ÀAÒ+EÜÐHÈ×I[ÑI[ÐH\Ð]Ó^Ð^Ø×Ñ˜aÑ  IÒ-ÜØHÈÈÐSZÐ[g×[mÑ[mÐnoÑ[pÐZqÐróð ð Ðr   c                óh  — g }g }g }g }|D ]Ÿ  }t        | |   «      D ]Œ  \  }}|j                  «       \  }	}
}|j                  t        |	«      t        |
«      f«       |j                  t        |«      «       |j                  t        |«      «       |j                  t        |«      «       ŒŽ Œ¡ ||||dœS )a<  Flatten per-instance keypoints into Albumentations keypoint fields.

        >>> keypoints = np.array([[[10.0, 20.0, 2.0], [0.0, 0.0, 0.0]]], dtype=np.float32)
        >>> fields = AlbumentationsWrapper._build_albu_keypoints(keypoints, [0])
        >>> fields["keypoints"]
        [(10.0, 20.0), (0.0, 0.0)]
        ©r   r¦   r§   r¨   )Ú	enumerateÚtolistrw   Úfloat)rÍ   r    Úalbu_keypointsÚinstance_idsÚ	point_idsÚ
visibilityÚoriginal_idxÚ	point_idxÚpointÚxÚyÚvisibles               r   Ú_build_albu_keypointsz+AlbumentationsWrapper._build_albu_keypointsé  sÁ   € ð 57ˆØ$&ˆØ!#ˆ	Ø"$ˆ
Ø ò 	2ˆLÜ$-¨l¸<Ñ.HÓ$Iò 2Ñ �	˜5Ø %§¡£‘��1�gØ×%Ñ%¤u¨Q£x´°q³Ð&:Ô;Ø×#Ñ#¤E¨,Ó$7Ô8Ø× Ñ ¤ yÓ!1Ô2Ø×!Ñ!¤%¨£.Õ1ñ2ð	2ð (Ø%1Ø"+Ø#-ñ	
ð 	
r   c                óö  — t        | t        «      sy| j                  d«      }t        |t        «      rt	        d„ |D «       «      S | j                  dd«      syt        | j                  dd«      «      j                  dd«      d	   }|d
k(  ry|dk(  r3| j                  d«      xs i }t        |j                  dd	«      «      dk(  S |dv r2| j                  d«      xs i }t        |j                  d«      «      dk(  S y)a  Return whether Albumentations replay metadata applied a horizontal flip.

        Args:
            replay: ``ReplayCompose`` metadata from an Albumentations call.

        Returns:
            ``True`` only when a horizontal mirror transform was actually applied.
        Fr\   c              3  óF   K  — | ]  }t         j                  |«      –— Œ y ­wr   )r›   Ú _replay_contains_horizontal_flip)r_   rf   s     r   ra   zIAlbumentationsWrapper._replay_contains_horizontal_flip.<locals>.<genexpr>  s   è ø€ ÒuÐ]fÔ,×MÑMÈi×XÑuùs   ‚!ÚappliedÚ__class_fullname__Ú ú.r   éÿÿÿÿrD   Tr5   r|   Úaxis>   r2   rG   Úgroup_elementr'   )ro   rr   rn   rp   re   ÚstrÚrsplitÚint)Úreplayr\   Útransform_namer|   s       r   rá   z6AlbumentationsWrapper._replay_contains_horizontal_flip  sï   € ô ˜&¤$Ô'Øà—Z‘Z Ó-ˆ
Ü�j¤$Ô'ÜÑuÐjtÔuÓuÐuà�z‰z˜) UÔ+Øä˜VŸZ™ZÐ(<¸bÓAÓB×IÑIÈ#ÈqÓQÐRTÑUˆØÐ-Ò-ØØ˜VÒ#Ø—Z‘Z Ó)Ò/¨RˆFÜ�v—z‘z &¨"Ó-Ó.°!Ñ3Ð3ØÐ5Ñ5Ø—Z‘Z Ó)Ò/¨RˆFÜ�v—z‘z /Ó2Ó3°sÑ:Ð:Ør   c           
     óP  — |j                   d   }t        j                  t        |«      |dft        j                  ¬«      }t        |«      D ��ci c]  \  }}t        |«      |“Œ }	}}| d   j                   dd \  }
}| j                  dg «      }|�rà| j                  dg «      }| j                  d	g «      }| j                  d
g «      }t        j                  |D �cg c]  }t        |d   «      t        |d   «      f‘Œ  c}t        j                  ¬«      }t        j                  |D �cg c]  }|	j                  t        |«      d«      ‘Œ c}t        j                  ¬«      }t        j                  |D �cg c]  }t        |«      ‘Œ c}t        j                  ¬«      }t        j                  |D �cg c]  }t        |«      ‘Œ c}t        j                  ¬«      }|dk\  |dk\  z  ||k  z  |dkD  z  |dd…df   dk\  z  |dd…df   |k  z  |dd…df   dk\  z  |dd…df   |
k  z  }t        j                  |«      d   }t        |«      dkD  r(t        j                  ||   ||   g«      |||   ||   f<   t        j                  |t        j                  ¬«      }|rs|rq|j                   d   }t        j                   |«      }t#        dt        |«      dz
  d«      D ]%  }||   ||dz      }}||k  sŒ||k  sŒ|||<   |||<   Œ' |dd…|dd…f   }|S c c}}w c c}w c c}w c c}w c c}w )a  Rebuild transformed keypoints and keep them synchronized with kept boxes.

        Args:
            augmented: Augmented output dict from Albumentations.
            kept_idxs: Original instance indices of surviving boxes.
            keypoints_np: Original keypoint array, shape (N_orig, K, 3).
            flip_pairs: Flat list of paired joint indices ``[a0, b0, a1, b1, ...]``
                to swap when a horizontal flip is detected.  Each consecutive pair
                ``(flip_pairs[i], flip_pairs[i+1])`` names two joints that are
                left/right mirrors of each other (e.g., left_eye, right_eye).
            did_flip: Whether a horizontal flip was applied this step.

        Returns:
            Keypoint tensor of shape ``(len(kept_idxs), K, 3)``.
        r   rË   r   r%   Nr‘   r   r¦   r§   r¨   r   ræ   )r    rÆ   Úzerosrs   r#   rÑ   rë   rn   ÚasarrayrÓ   rÇ   ÚintpÚwhereÚcolumn_stackr!   Ú	as_tensorÚarangeÚrange)Ú	augmentedÚ	kept_idxsrÍ   Ú
flip_pairsÚdid_flipÚnum_keypointsÚkeypoints_outÚpositionrØ   Úkept_position_by_idxr�   r�   Úalbu_kpsÚinst_idsÚpt_idsrÝ   rj   r¥   ÚiiÚinstÚptidrƒ   ÚvisÚvalidÚ	valid_idxÚresultÚnum_kptsÚpermÚiÚaiÚbis                                  r   Ú_rebuild_keypoints_from_albuz2AlbumentationsWrapper._rebuild_keypoints_from_albu&  s  € ð. %×*Ñ*¨1Ñ-ˆÜŸ™¤# i£.°-ÀÐ!CÌ2Ï:É:ÔVˆÜZcÐdmÓZn×oÑ@VÀÈ,¤ LÓ 1°8Ñ ;ÐoÐÑoØ! 'Ñ*×0Ñ0°°!Ð4‰ˆ�à—=‘= ¨bÓ1ˆÚØ —}‘}Ð%<¸bÓAˆHØ—]‘]Ð#7¸Ó<ˆFØ—m‘mÐ$9¸2Ó>ˆGÜ—‘ÀÖJ¸Aœe A a¡D›k¬5°°1±«;Ò7ÒJÔRT×R\ÑR\Ô]ˆBÜ—8‘8ÈXÖVÀrÐ1×5Ñ5´c¸"³g¸rÕBÒVÔ^`×^eÑ^eÔfˆDÜ—8‘8¨VÖ4¨œS �VÒ4¼B¿G¹GÔDˆDÜ—*‘*°Ö8¨1œe A�hÒ8ÄÇ
Á
ÔKˆCà˜‘Ø˜1‘9ñà˜-Ñ'ñ)ð ˜‘7ñð ’a˜�d‘8˜q‘=ñ	"ð
 ’a˜�d‘8˜eÑ#ñ%ð ’a˜�d‘8˜q‘=ñ"ð ’a˜�d‘8˜fÑ$ñ&ð ô Ÿ™ ›¨Ñ*ˆIÜ�9‹~ Ò!ÜBDÇ/Á/ÐSUÐV_ÑS`ÐbeÐfoÑbpÐRqÓBr�˜d 9™o¨t°I©Ð>Ñ?ä—‘ ´e·m±mÔDˆá™
à—|‘| A‘ˆHÜ—<‘< Ó)ˆDÜ˜1œc *›o°Ñ1°1Ó5ò "�Ø# A™¨
°1°q±5Ñ(9�B�Ø˜“= R¨(£]Ø!�D˜‘HØ!�D˜’Hð	"ð
 šA˜t¢Q˜JÑ'ˆFàˆùóO  pùò KùÚVùÚ4ùÚ8s   ÁLÃ#LÄ("LÅ4LÆ0L#c                ó€  — h d£}i }| j                  «       D ]¤  \  }}||v rŒt        j                  |«      rI|j                  dk\  sŒ0|j                  d   |k(  sŒC|j                  dg|j                  dd ¢­«      ||<   Œit        |t        «      sŒzt        |t        t        f«      rŒ‘t        |«      |k(  sŒ g ||<   Œ¦ |S )a;  Clear all per-instance fields when no boxes remain.

        >>> import torch
        >>> target = {"area": torch.tensor([100, 200]), "iscrowd": torch.tensor([0, 1])}
        >>> cleared = AlbumentationsWrapper._clear_per_instance_fields(target, 2)
        >>> cleared["area"].shape
        torch.Size([0])
        >   r‰   r   ÚlabelsÚimage_idÚ	orig_sizer   r   N)rv   r!   rÃ   Úndimr    Ú	new_emptyro   r   ré   Úbytesrs   )r&   rÌ   Úglobal_fieldsr  ÚkeyÚvalues         r   Ú_clear_per_instance_fieldsz0AlbumentationsWrapper._clear_per_instance_fieldsh  s°   € ò MˆàˆØ Ÿ,™,›.ò 	%‰JˆC�Ø�mÑ#ØÜ�‰˜uÔ%Ø—:‘: “? u§{¡{°1¡~¸Ó'BØ"'§/¡/°1Ð2G°u·{±{À1À2°Ñ2GÓ"H�F˜3’KÜ˜E¤8Õ,´ZÀÌÌUÀ|Õ5TÜ�u“: Ó*Ø"$�F˜3’Kð	%ð ˆr   c                ó¼  — h d£}i }t        j                  |t         j                  ¬«      }| j                  «       D ]˜  \  }}||v rŒt        j                  |«      r,|j
                  dk\  sŒ0|j                  d   |k(  sŒC||   ||<   ŒLt        |t        «      sŒ]t        |t        t        f«      rŒtt        |«      |k(  sŒƒ|D �cg c]  }||   ‘Œ	 c}||<   Œš |S c c}w )aN  Filter per-instance fields to match kept box indices.

        >>> import torch
        >>> target = {"area": torch.tensor([100, 200, 300]), "iscrowd": torch.tensor([0, 0, 1])}
        >>> filtered = AlbumentationsWrapper._filter_per_instance_fields(target, 3, [0, 2])
        >>> filtered["area"].tolist()
        [100, 300]
        >   r‰   r   r  r  r  r   r   r   )r!   rô   Úlongrv   rÃ   r  r    ro   r   ré   r  rs   )	r&   rÌ   rø   r  r  Úkept_idxs_tensorr  r  r  s	            r   Ú_filter_per_instance_fieldsz1AlbumentationsWrapper._filter_per_instance_fields�  sÇ   € ò MˆàˆÜ Ÿ?™?¨9¼E¿J¹JÔGÐØ Ÿ,™,›.ò 	@‰JˆC�Ø�mÑ#ØÜ�‰˜uÔ%Ø—:‘: “? u§{¡{°1¡~¸Ó'BØ"'Ð(8Ñ"9�F˜3’KÜ˜E¤8Õ,´ZÀÌÌUÀ|Õ5TÜ�u“: Ó*Ø5>Ö"?° 5¨£8Ò"?�F˜3’Kð	@ð ˆùò #@s   ÃCc                óÌ	  — | j                  |d   «      }|j                  d   }t        t        |«      «      }d}d|v r¤|d   }t	        j
                  |«      r|j                  «       j                  «       nt        j                  |«      }	|	j                  dk7  rt        d|	j                  › �«      ‚|	j                  t        j                  d¬«      }	|	D �
cg c]  }
|
‘Œ }}
d}d	|v r| j                  |d	   |«      }|dkD  r†|dd…d
f   |dd…df   kD  |dd…df   |dd…df   kD  z  }|j                  «       sOt        j                   |«      d   j#                  «       }||   }|D �cg c]  }||   ‘Œ	 }}|D �cg c]  }||   ‘Œ	 }}||||dœ}|�t%        |«      dkD  r||d<   |�"|j'                  | j)                  ||«      «       n|j'                  g g g g dœ«        | j*                  di |¤Ž}|j-                  «       }|d   }|j/                  d|«      D �cg c]  }t1        |«      ‘Œ }}t%        |«      dk(  r¸t	        j2                  dt        j4                  ¬«      |d<   t	        j2                  dt        j6                  ¬«      |d<   |j'                  | j9                  ||«      «       d|v �rM|d   j                  dd
 \  }}t	        j2                  d||ft        j:                  ¬«      |d<   �nt	        j<                  |t        j4                  ¬«      j?                  dd«      |d<   t	        j@                  |d   t        j6                  ¬«      |d<   |j'                  | jC                  |||«      «       d|v r/|d   }|dd…d
f   |dd…df   z
  |dd…df   |dd…df   z
  z  |d<   |�Q| jD                  r | jG                  |j/                  d«      «      nd}| jI                  |||| jD                  |¬«      |d	<   tK        jL                  |d   «      }|�±d|v r­|d   j                  dd
 \  }}|d   }|D �cg c]  }|t1        |«         ‘Œ }}t%        |«      dk(  r/t	        j2                  d||ft        j:                  ¬«      |d<   ||fS t	        j<                  t        jN                  |«      t        j:                  ¬«      |d<   ||fS c c}
w c c}w c c}w c c}w c c}w )aÇ  Apply geometric transform to image with boxes and optionally masks.

        Converts data to Albumentations format, applies the transform, and converts back to RF-DETR format. Handles box
        removal and per-instance field filtering.

        Args:
            image_np: Numpy array of image in HWC format.
            target: Target dictionary with 'boxes' and optionally 'masks'.
            labels: List of category labels.

        Returns:
            Tuple of (transformed PIL Image, transformed target dict).

        >>> import torch
        >>> from albumentations import HorizontalFlip
        >>> wrapper = AlbumentationsWrapper(HorizontalFlip(p=1.0))
        >>> img = np.ones((100, 100, 3), dtype=np.uint8)
        >>> tgt = {"boxes": torch.tensor([[10, 20, 30, 40]]), "labels": torch.tensor([1])}
        >>> img_out, tgt_out = wrapper._apply_geometric_transform(img, tgt, [1])
        >>> tgt_out["boxes"].shape
        torch.Size([1, 4])
        r   r   NÚmasksrË   z%masks must have shape (N, H, W), got F)r   r   r‘   r   )r%   ÚbboxesrŸ   r    rÐ   r   r    )r   rÁ   r   )r   r  r%   ræ   rÁ   rŸ   Úarearì   )rù   rú   r1   )(rÉ   r    rp   rö   r!   rÃ   rÄ   rÅ   rÆ   rÇ   r  rq   ÚastypeÚuint8rÎ   Úallrò   rÒ   rs   ÚupdaterÞ   rf   r   rn   rë   rï   r#   r  r  r®   rô   Úreshaper"   r  r­   rá   r  r   Ú	fromarrayÚstack)r   Úimage_npr&   r  rÈ   rÌ   r    Ú
masks_listr  Úmasks_npÚmaskrÍ   Ú
valid_maskÚvalid_positionsr  Útransform_kwargsr÷   Ú
target_outÚ
bboxes_augÚidxrø   Ú
aug_heightÚ	aug_widthr   rú   Ú	image_outr�   r�   Ú	masks_augs                                r   Ú_apply_geometric_transformz0AlbumentationsWrapper._apply_geometric_transform›  sç  € ð2 ×'Ñ'¨¨w©Ó8ˆØ—N‘N 1Ñ%ˆ	ä”E˜)Ó$Ó%ˆØˆ
Ø�fÑØ˜7‘OˆEÜ.3¯o©o¸eÔ.D�u—y‘y“{×(Ñ(Ô*Ì"Ï(É(ÐSXË/ˆHØ�}‰} Ò!Ü Ð#HÈÏÉÐHXÐ!YÓZÐZØ—‘¤r§x¡x°e�Ó<ˆHØ+3Ö4 4š$Ð4ˆJÐ4ØˆØ˜&Ñ Ø×3Ñ3°F¸;Ñ4GÈÓSˆLð
 �qŠ=Ø"¢1 a 4™.¨8²A°q°D©>Ñ9¸hÂqÈ!Àt¹nÈxÒXYÐ[\ÐX\É~Ñ>]Ñ^ˆJØ—>‘>Ô#Ü"$§(¡(¨:Ó"6°qÑ"9×"@Ñ"@Ó"B�Ø# JÑ/�Ø-<Ö=¨˜& ›)Ð=�Ð=ð *9Ö9 A˜˜Q›Ð9�Ð9à%-¸ÐSYÐcgÑhÐØÐ!¤c¨*£o¸Ò&9Ø(2Ð˜WÑ%ØÐ#Ø×#Ñ# D×$>Ñ$>¸|ÈTÓ$RÕSà×#Ñ#à!#Ø-/Ø*,Ø+-ñ	ôð #�D—N‘NÑ6Ð%5Ñ6ˆ	Ø%+§[¡[£]ˆ
Ø˜xÑ(ˆ
Ø)2¯©°v¸tÓ)DÖE #”S˜•XÐEˆ	ÐEäˆz‹?˜aÒÜ"'§+¡+¨f¼E¿M¹MÔ"JˆJ�wÑÜ#(§;¡;¨t¼5¿:¹:Ô#FˆJ�xÑ Ø×Ñ˜d×=Ñ=¸fÀiÓPÔQà˜&Ò Ø(1°'Ñ(:×(@Ñ(@ÀÀ!Ð(DÑ%�
˜IÜ&+§k¡k°1°jÀ)Ð2LÔTY×T^ÑT^Ô&_�
˜7Ó#ä"'§/¡/°*ÄEÇMÁMÔ"R×"ZÑ"ZÐ[]Ð_`Ó"aˆJ�wÑÜ#(§<¡<°	¸.Ñ0IÔQV×Q[ÑQ[Ô#\ˆJ�xÑ Ø×Ñ˜d×>Ñ>¸vÀyÐR[Ó\Ô]ð ˜Ñ#Ø" 7Ñ+�Ø&+ªA¨q¨D¡k°Eº!¸Q¸$±KÑ&?ÀEÊ!ÈQÈ$ÁKÐRWÒXYÐ[\ÐX\ÑR]ÑD]Ñ%^�
˜6Ñ"ØÐ'ð ×0Ò0ð ×9Ñ9¸)¿-¹-ÈÓ:QÔRàð ð
 +/×*KÑ*KØØØ Ø#×8Ñ8Ø%ð +Ló +�
˜;Ñ'ô —O‘O I¨gÑ$6Ó7ˆ	ØÐ! g°Ñ&:Ø% gÑ.×4Ñ4°R°aÐ8‰MˆF�EØ! 'Ñ*ˆIØ4=Ö>¨q˜¤3 q£6Ó*Ð>ˆIÐ>Ü�9‹~ Ò"Ü&+§k¡k°1°f¸eÐ2DÌEÏJÉJÔ&W�
˜7Ñ#ð ˜*Ð$Ð$ô ',§o¡o´b·h±h¸yÓ6IÔQV×Q[ÑQ[Ô&\�
˜7Ñ#Ø˜*Ð$Ð$ùòY 5ùò >ùò :ùò& FùòH ?s   Ã	SÅ(SÅ:SÈSÐ:S!c           
     óŽ  — |€gt        j                  |«      }| j                  r| j                  |g g g g g g g ¬«      }n| j                  |¬«      }t	        j
                  |d   «      dfS t        |t        «      st        dt        |«      › �«      ‚d|vrt        d«      ‚t        j                  |«      }t        j                  |d   «      r!|d   j                  «       j                  «       nt        |d   «      }| j                  rd|v rd	|vrt         j#                  d
«       | j                  rd	|v r| j%                  |||«      \  }}n:| j                  |¬«      }t	        j
                  |d   «      }|j'                  «       }d|v r9|j(                  \  }}	t        j*                  |	|gt        j,                  ¬«      |d<   ||fS )a	  Apply the Albumentations transform to image and target.

        This method handles the data format conversion between RF-DETR and Albumentations:
        1. Converts PIL Image to numpy array (required by Albumentations)
        2. Converts PyTorch tensors to numpy/lists (required by Albumentations)
        3. Applies the transform
        4. Converts results back to PIL Image and PyTorch tensors

        For geometric transforms with bounding boxes, this method also:
        - Validates box shapes and coordinates
        - Handles boxes that may be removed by the transform (e.g., cropped out)
        - Ensures labels stay synchronized with their corresponding boxes
        - Transforms masks when present to stay aligned with the image

        Args:
            image: Input PIL Image in RGB format.
            target: Target dictionary containing:
                - 'labels': PyTorch tensor of shape (N,) with class labels
                - 'boxes' (optional): PyTorch tensor of shape (N, 4) in (x1, y1, x2, y2) format
                - 'masks' (optional): PyTorch tensor of shape (N, H, W) with instance segmentation masks.
                  For geometric transforms, masks are transformed alongside boxes to maintain alignment. Requires
                  'boxes' to be present; a warning is logged if masks exist without boxes.
                Pass ``None`` for inference scenarios where no ground-truth annotations are available.

        Returns:
            Tuple of (transformed_image, transformed_target):
                - transformed_image: PIL Image after augmentation
                - transformed_target: Dictionary with augmented boxes and labels, or ``None`` if
                  ``target`` was ``None``.

        Raises:
            TypeError: If target is not a dictionary (and not None).
            KeyError: If target doesn't contain 'labels' key.
            ValueError: If boxes don't have shape (N, 4).

        Examples:
            >>> from albumentations import HorizontalFlip
            >>> wrapper = AlbumentationsWrapper(HorizontalFlip(p=1.0))
            >>> image = Image.new('RGB', (100, 100))
            >>> target = {"boxes": torch.tensor([[10, 20, 90, 80]]), "labels": torch.tensor([1])}
            >>> aug_image, aug_target = wrapper(image, target)
        N)r%   r   rŸ   r    r   r¦   r§   r¨   )r%   r%   z!target must be a dictionary, got r  z target must contain 'labels' keyr  r   z¦AlbumentationsWrapper: geometric transform requested with 'masks' but without 'boxes'. Masks will not be geometrically transformed because bounding boxes are missing.r‰   r   )rÆ   rÇ   r¬   rf   r   r'  ro   rr   Ú	TypeErrorrb   ÚKeyErrorr!   rÃ   rÄ   rÒ   rp   r¯   r°   r7  r   r‰   rô   Úint64)
r   r%   r&   r)  r÷   r  r5  r0  r�   r�   s
             r   r)   zAlbumentationsWrapper.__call__  s¿  € ð\ ˆ>Ü—x‘x “ˆHØ×!Ò!à ŸN™NØ"ØØ!#ØØ Ø*,Ø')Ø(*ð +ó 	‘	ð !ŸN™N°˜NÓ:�	Ü—?‘? 9¨WÑ#5Ó6¸Ð<Ð<ô ˜&¤$Ô'ÜÐ?ÄÀVÃ¸~ÐNÓOÐOØ˜6Ñ!ÜÐ=Ó>Ð>ô —8‘8˜E“?ˆô 5:·O±OÀFÈ8ÑDTÔ4U�˜Ñ!×%Ñ%Ó'×.Ñ.Ô0Ô[_Ð`fÐgoÑ`pÓ[qˆð ×Ò '¨VÑ"3¸ÀvÑ8MÜ�N‰Nðbôð ×Ò '¨VÑ"3à$(×$CÑ$CÀHÈfÐV\Ó$]Ñ!ˆI‘zð Ÿ™¨X˜Ó6ˆIÜŸ™¨	°'Ñ(:Ó;ˆIØŸ™›ˆJð �ZÑà%ŸN™N‰MˆE�6Ü!&§¡°&¸%°ÌÏÉÔ!TˆJ�vÑØ˜*Ð$Ð$r   c                ó   — t        | t        t        f«      xr t        | «      dk(  }t	        | |duxr | t
        j                  ¬«      } t        | t        «      r| }nLt        | t        «      r%| j                  «       D ��cg c]	  \  }}||i‘Œ }}}nt        dt        | «      › �«      ‚|s|sg S t
        j                  d«       g S t        €t        d«      ‚g }|D ]Ü  }t        |t        «      rt        |«      dk7  rt
        j                  d|«       Œ8t        t        |j                  «       «      «      \  }}	t        |	t        «      r|t        v rd	|	i}	t        |	t        «      s+t
        j                  d
|t        |	«      j                  «       Œ´	 t!        ||	«      }
|j#                  t%        |
|¬«      «       ŒÞ t
        j)                  dt        |«      «       |S c c}}w # t&        $ r#}t
        j                  d||	|«       Y d}~�Œ-d}~ww xY w)ul  Build a list of :class:`AlbumentationsWrapper` instances from a config.

        Supports both a flat dictionary format (backward-compatible) and a list format that allows duplicate transform
        names and explicit ordering. Container transforms (``OneOf``, ``SomeOf``, ``Sequential``) may be nested
        arbitrarily deep.

        **Dict format** (existing, backward-compatible)::

            config = {
                "HorizontalFlip": {"p": 0.5},
                "Rotate": {"limit": 45, "p": 0.3},
                "OneOf": {
                    "transforms": [
                        {"HorizontalFlip": {"p": 1.0}},
                        {"VerticalFlip": {"p": 1.0}},
                    ],
                },
            }

        **List format** (new; useful when you need two entries with the same name or when explicit order matters)::

            config = [
                {"HorizontalFlip": {"p": 0.5}},
                {"OneOf": {
                    "transforms": [
                        {"Rotate": {"limit": 45, "p": 1.0}},
                        {"ShiftScaleRotate": {"p": 1.0}},
                    ],
                }},
            ]

        **Shorthand for container ``transforms`` list** -- when a container key's value is a *list* rather than a dict,
        it is interpreted as the ``transforms`` parameter::

            {"OneOf": [{"HorizontalFlip": {"p": 1.0}}, {"VerticalFlip": {"p": 1.0}}]}

        Args:
            config_dict: Augmentation configuration -- either a ``dict`` mapping
                transform names to parameter dicts, or a ``list`` of single-key dicts ``{name: params}``.
            keypoint_flip_pairs: Joint index pairs for swapping left/right keypoints after a horizontal
                flip (e.g. ``[0, 1, 2, 3]`` swaps joint 0â†”1 and 2â†”3). Pass ``None`` (default) for
                detection pipelines where horizontal flips are always permitted. Pass an empty list
                ``[]`` to mark a keypoint pipeline without any defined flip pairs -- horizontal-flip
                augmentations are then disabled until flip-pair swapping is implemented.

        Returns:
            List of :class:`AlbumentationsWrapper` instances in config order.

        Raises:
            ImportError: If Albumentations is not installed.
            TypeError: If *config_dict* is neither a ``dict`` nor a ``list``.

        Examples:
            >>> config = {
            ...     "HorizontalFlip": {"p": 0.5},
            ...     "Rotate": {"limit": 45, "p": 0.3},
            ...     "GaussianBlur": {"p": 0.2}
            ... }
            >>> transforms = AlbumentationsWrapper.from_config(config)
            >>> [t.transform.transforms[0].__class__.__name__ for t in transforms]
            ['HorizontalFlip', 'Rotate', 'GaussianBlur']

        Note:
            Invalid transforms or invalid parameters are logged and skipped gracefully.
        r   N)Úinclude_keypointsÚwarnz.config_dict must be a dictionary or list, got zAEmpty augmentation config provided, no transforms will be appliedrh   r   z=Skipping invalid config entry (must be a single-key dict): %rr\   z4Skipping %s: parameters must be a dictionary, got %s)r´   z5Failed to initialize %s with params %r: %s. Skipping.z.Built %d Albumentations transforms from config)ro   rr   rp   rs   r
   r¯   r°   rv   r9  rb   rk   rl   rt   ru   rm   r.   rx   rw   r›   Ú	ExceptionÚinfo)Úconfig_dictr´   Úoriginal_config_emptyÚentriesr‚   rƒ   r\   r   Úaug_namer|   rf   Úes               r   Úfrom_configz!AlbumentationsWrapper.from_configp  sú  € ôL !+¨;¼¼t¸Ó EÒ _Ì#ÈkÓJZÐ^_ÑJ_ÐÜ9ØØ1¸Ð=ÒYÐFYÐBYÜ—‘ô
ˆô
 �k¤4Ô(Ø!‰GÜ˜¤TÔ*Ø*5×*;Ñ*;Ó*=×>¡$ ! Q˜˜1’vÐ>ˆGÒ>äÐLÌTÐR]ÓM^ÐL_Ð`ÓaÐaáÙ(Ø�	Ü�N‰NÐ^Ô_ØˆIäˆ;Üðjóð ð
 ˆ
Øò 	ˆEÜ˜e¤TÔ*¬c°%«j¸AªoÜ—‘ØSØôð Ü#¤D¨¯©«Ó$7Ó8ÑˆH�fô ˜&¤$Ô'¨HÔ8QÑ,QØ&¨Ð/�ä˜f¤dÔ+Ü—‘ØJØÜ˜“L×)Ñ)ôð
 ð
Ü1°(¸FÓC�	Ø×!Ñ!Ô"7¸	ÐWjÔ"kÕlð/	ôB 	�‰ÐDÄcÈ*ÃoÔVØÐùóg ?øôR ò Ü—‘ØKØØØô	õ ûðús   Â GÆ(G!Ç!	HÇ*HÈHr   )rf   úalb.BasicTransformr´   úlist[int] | Noner+   r,   )r+   ré   )r   úTensor | np.ndarrayr+   ú
np.ndarray)r   rI  rÌ   rë   r+   rJ  )rÍ   rJ  r    ú	list[int]r+   údict[str, Any])rì   r   r+   r®   )NF)r÷   rL  rø   rK  rÍ   rJ  rù   rH  rú   r®   r+   r   )r&   rL  rÌ   rë   r+   rL  )r&   rL  rÌ   rë   rø   rK  r+   rL  )r)  rJ  r&   rL  r  rK  r+   z"tuple[Image.Image, dict[str, Any]])r%   zPIL.Image.Imager&   r-   r+   z-tuple[PIL.Image.Image, dict[str, Any] | None])rA  z%dict[str, Any] | list[dict[str, Any]]r´   rH  r+   zlist['AlbumentationsWrapper'])r.   r/   r0   Ú__doc__r   r¼   ÚstaticmethodrÉ   rÎ   rÞ   rá   r  r  r  r7  r)   rF  r1   r   r   r›   r›   j  s€  „ ñ%ôN 6óDZð* òó ðð òó ðð" ð
Ø ð
àð
ð 
ò
ó ð
ð: òó ðð< ð
 (,Øð?Ø!ð?àð?ð !ð?ð %ð	?ð
 ð?ð 
ò?ó ð?ðB òó ðð0 òó ðð2p%Ø"ðp%Ø,:ðp%ØDMðp%à	+óp%ðda%Ø$ða%Ø.Cða%à	6óa%ðF ð 15ðAØ:ðAà-ðAð 
'òAó ñAr   r›   )rf   rG  r+   r®   )r{   ré   r|   rL  r+   rG  )r†   rb   r+   r®   )r{   ré   r|   rL  r†   rb   r+   rL  )&rM  Ú
__future__r   rŠ   Úcollections.abcr   Ú	functoolsr   Útypingr   Úalbumentationsrk   rl   rÅ   rÆ   ÚPILr!   r   r   Útorchvision.transformsr	   r   Úrfdetr.datasets._aug_utilsr
   Úrfdetr.utilities.box_opsr   Úrfdetr.utilities.loggerr   r¯   r»   rc   Ú	frozensetrm   r^   rx   r�   rz   r›   r1   r   r   ú<module>rZ     s¹   ðñ >å "ã Ý $Ý Ý ðÛ ó Û 
Û Ý Ý Ý <å JÝ 7Ý .á	‹€ô3�ô 3òv-Ð ñ` &Ò&GÓHÐ óó:KDñ\ �4Ôò*ó ð*ó"R÷jH
ò H
øða
 ò Ø
‚Cðús    B ÂB%Â$B%