
    ^jQ              	         U d Z ddlmZ ddlm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  e       Zd	d
giZdZdZdZded<   d#dZd$dZd%dZd&dZd&dZd&dZd&dZd&dZd&dZd&dZd&dZeeeeeeeedZded<   	 	 d'	 	 	 	 	 	 	 	 	 d(dZeef	 	 	 	 	 d)dZ 	 	 	 	 	 	 d*d Z!	 	 	 	 	 	 	 	 	 	 	 	 d+d!Z"	 d,	 	 	 	 	 	 	 	 	 	 	 	 	 d-d"Z#y).ab  Kornia-based GPU augmentation pipeline for RF-DETR training.

This module provides GPU-side augmentation as an alternative to the CPU-based Albumentations pipeline.  All transforms
run on the device where the batch already resides (typically CUDA), avoiding a CPU-GPU round-trip per sample.

Supports detection (boxes only) and segmentation (boxes + instance masks).

Usage::

    from rfdetr.datasets.kornia_transforms import (
        build_kornia_pipeline,
        build_normalize,
        collate_boxes,
        collate_masks,
        unpack_boxes,
    )

    # Detection:
    pipeline = build_kornia_pipeline(aug_config, resolution=560)
    normalize = build_normalize()
    boxes_padded, valid = collate_boxes(targets, device)
    img_aug, boxes_aug = pipeline(img, boxes_padded)
    img_aug = normalize(img_aug)
    targets = unpack_boxes(boxes_aug, valid, targets, H, W)

    # Segmentation (Phase 2):
    pipeline = build_kornia_pipeline(aug_config, resolution=560, with_masks=True)
    normalize = build_normalize()
    boxes_padded, valid = collate_boxes(targets, device)
    masks_padded = collate_masks(targets, device, n_max=valid.shape[1], image_height=H, image_width=W)
    img_aug, boxes_aug, masks_aug = pipeline(img, boxes_padded, masks_padded)
    img_aug = normalize(img_aug)
    targets = unpack_boxes(boxes_aug, valid, targets, H, W, masks_aug=masks_aug)
    )annotations)Callable)AnyN)Tensor)#filter_keypoint_hflip_augmentations)
get_loggerbuild_kornia_pipelinekornia)g
ףp=
?gv/?gCl?)gZd;O?gy&1?g?      ?float_MASK_BINARIZE_THRESHOLDc                 B    ddl m}  t        |       j                  d      S )a  Return ``True`` when the runtime has a CUDA accelerator available.

    Uses the fork-safe global ``DEVICE`` constant from ``rfdetr.config`` so that the CUDA driver context is not created
    in the main process before forking (fork-based DDP and some notebook environments).

    Returns:
        ``True`` if at least one CUDA device is reachable; ``False`` otherwise.

    Examples:
        >>> _has_cuda_device()  # doctest: +SKIP
        False
    r   DEVICEcuda)rfdetr.configr   str
startswithr   s    l/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/datasets/kornia_transforms.py_has_cuda_devicer   D   s     %v;!!&))    c                    | dk(  ry| dk(  rt               sy	 ddl}y| dk(  r t               st        d      t	                yt        d| d      # t        $ r Y yw xY w)	a  Resolve an ``augmentation_backend`` value to a concrete ``"cpu"`` or ``"gpu"``.

    ``"auto"`` resolves to ``"gpu"`` only when both CUDA and Kornia are available; otherwise it falls back to ``"cpu"``.
    Explicit ``"cpu"`` and ``"gpu"`` values pass through unchanged; ``"gpu"`` is validated (CUDA + kornia presence).

    Args:
        backend: One of ``"cpu"``, ``"auto"``, or ``"gpu"``.

    Returns:
        ``"cpu"`` or ``"gpu"``.

    Raises:
        RuntimeError: When *backend* is ``"gpu"`` and no CUDA device is found.
        ImportError: When *backend* is ``"gpu"`` and kornia is not installed.
        ValueError: When *backend* is not one of ``"cpu"``, ``"auto"``, or ``"gpu"``.

    Examples:
        >>> resolve_augmentation_backend("cpu")
        'cpu'
    cpuautor   Ngpuz1augmentation_backend='gpu' requires a CUDA devicezUnknown augmentation_backend z#; expected 'cpu', 'auto', or 'gpu'.)r   kornia.augmentationImportErrorRuntimeError_require_kornia
ValueError)backendr
   s     r   resolve_augmentation_backendr"   V   sz    * %&!	& %!RSS
4WK?bc
dd  		s   A 	AAc                 H    	 ddl } y# t        $ r}t        d      |d}~ww xY w)zVerify that Kornia is importable, raising a clear error if not.

    Raises:
        ImportError: When ``kornia`` is not installed, with an install hint.
    r   NzLGPU augmentation requires kornia. Install with: pip install 'rfdetr[kornia]')r   r   )r
   es     r   r   r   }   s,    q" qhioppqs    	!!c                @    ddl m}  || j                  dd            S )z:Build a ``K.RandomHorizontalFlip`` from aug_config params.r   )RandomHorizontalFlippr   r'   )r   r&   get)paramsr&   s     r   _make_horizontal_flipr+      s    8&**S#"677r   c                @    ddl m}  || j                  dd            S )z8Build a ``K.RandomVerticalFlip`` from aug_config params.r   )RandomVerticalFlipr'   r   r(   )r   r-   r)   )r*   r-   s     r   _make_vertical_flipr.      s    6

3 455r   c                   ddl m} | j                  dd      }t        |t        t
        f      rt        |      n| |f} ||| j                  dd            }t        |dd	      }t        |t              r	d
|vr||d
<   |S )zBuild a ``K.RandomRotation`` from aug_config params.

    The ``limit`` parameter may be a scalar (symmetric range) or a tuple.
    r   )RandomRotationlimit   r'   r   )degreesr'   flagsNr3   )r   r0   r)   
isinstancelisttuplegetattrdict)r*   r0   r1   r3   rotationr4   s         r   _make_rotater;      s|    
 3JJw#E(u>eEleVUOGgC1EFH
 Hgt,E%9E#9"iOr   c           
     n   ddl m} | j                  d      }|Pt        |t        t
        f      r7t        |      dk(  r)t        t        |d         t        |d               }||f}n|}nd} || j                  dd      || j                  d	      | j                  d
      | j                  dd            S )a  Build a ``K.RandomAffine`` from aug_config params.

    Albumentations ``translate_percent`` is a ``(min, max)`` signed range (e.g. ``(-0.1, 0.1)``).  Kornia ``translate``
    is a non-negative per-axis max fraction ``(tx, ty)`` where translation is sampled from ``[-tx, tx]``.  The
    conversion takes ``max(|min|, |max|)`` for each axis, producing a symmetric range that matches the intent.
    r   )RandomAffinetranslate_percentN      rotate)ir2   scaleshearr'   r   )r3   	translaterB   rC   r'   )	r   r=   r)   r5   r6   r7   lenmaxabs)r*   r=   r>   trD   s        r   _make_affinerI      s     1

#67$'$7C@Q<RVW<WC)!,-s3DQ3G/HIA=>FI)I	

8Y/jj!jj!
**S#
 r   c           
         ddl m}  || j                  dd      | j                  dd      | j                  dd      | j                  dd      | j                  dd	      
      S )zBuild a ``K.ColorJiggle`` from aug_config ``ColorJitter`` params.

    Note: Kornia >=0.7 uses ``ColorJiggle``; the ``ColorJitter`` alias was
    added in later versions.  We use ``ColorJiggle`` for broad compatibility.
    r   ColorJiggle
brightnessg        contrast
saturationhuer'   r   )rM   rN   rO   rP   r'   r   rL   r)   r*   rL   s     r   _make_color_jitterrS      sZ     0::lC0J,::lC0JJuc"
**S#
 r   c                    ddl m}  || j                  dd      | j                  dd      | j                  dd            S )	zCBuild a ``K.ColorJiggle`` from ``RandomBrightnessContrast`` params.r   rK   brightness_limitg?contrast_limitr'   r   )rM   rN   r'   rQ   rR   s     r    _make_random_brightness_contrastrW      s>    /::0#6,c2
**S#
 r   c                    ddl m} | j                  dd      }|dz  dk(  r|dz   }t        d|      } |||fd| j                  dd	      
      S )zBuild a ``K.RandomGaussianBlur`` from aug_config params.

    ``blur_limit`` is rounded up to an odd number for the kernel size.
    r   )RandomGaussianBlur
blur_limit   r?   r@   )g?g       @r'   r   )kernel_sizesigmar'   )r   rY   r)   rF   )r*   rY   rZ   s      r   _make_gaussian_blurr^      s^    
 7L!,JA~!^
Q
#J,
**S#
 r   c                l    ddl m} | j                  dd      } ||d   | j                  dd            S )	zBuild a ``K.RandomGaussianNoise`` from aug_config params.

    Kornia takes a single ``std`` value; we use the upper bound of ``std_range`` as an acceptable approximation.
    r   )RandomGaussianNoise	std_range)g{Gz?g?r@   r'   r   )stdr'   )r   r`   r)   )r*   r`   ra   s      r   _make_gauss_noiserc      s9    
 8

;5IaL
**S#
 r   )HorizontalFlipVerticalFlipRotateAffineColorJitterRandomBrightnessContrastGaussianBlur
GaussNoisez*dict[str, Callable[[dict[str, Any]], Any]]	_REGISTRYc           	     p   t                ddlm} t        | |t        j
                        }t        |t              sJ g }|j                         D ]R  \  }}t        j                  |      }	|	t        d|dt        t               d      |j                   |	|             T |rg dndd	g}
 ||d
|
iS )a  Build a Kornia ``AugmentationSequential`` from an aug_config dict.

    Each key in *aug_config* is looked up in ``_REGISTRY`` and instantiated with the corresponding parameter dict.
    Unknown keys raise ``ValueError``.

    Args:
        aug_config: Mapping of augmentation names to parameter dicts, identical
            to the format accepted by the Albumentations path (e.g. ``{"HorizontalFlip": {"p": 0.5}}``).
        resolution: Target image resolution in pixels (currently reserved for
            future resolution-aware augmentations).
        with_masks: When ``True``, include ``"mask"`` in ``data_keys`` so
            instance segmentation masks are augmented in sync with images and boxes.  The pipeline then expects three
            inputs ``(img, boxes, masks)`` and returns three outputs.  Defaults to ``False`` (detection-only, two
            inputs/outputs).
        include_keypoints: When ``True``, keypoint-unsafe horizontal-flip
            transforms are dropped with a warning before the Kornia pipeline is built.

    Returns:
        A ``kornia.augmentation.AugmentationSequential`` instance.

    Raises:
        ValueError: If *aug_config* contains an unsupported augmentation key.

    Examples:
        >>> from rfdetr.datasets.aug_configs import AUG_CONSERVATIVE
        >>> pipeline = build_kornia_pipeline(AUG_CONSERVATIVE, resolution=560)
        >>> pipeline_seg = build_kornia_pipeline(AUG_CONSERVATIVE, resolution=560, with_masks=True)
    r   )AugmentationSequential)include_keypointswarnzUnknown augmentation key z) for Kornia GPU backend. Supported keys: .)input	bbox_xyxymaskrr   rs   	data_keys)r   r   rn   r   loggerwarningr5   r9   itemsrl   r)   r    sortedappend)
aug_config
resolution
with_masksro   rn   filtered_aug_config
transformsnamer*   factoryru   s              r   r	   r	     s    D :=+^^
 )4000J+113 +f--%?+D83\]cdm]n\oopq  	'&/*+ 3=.7KBXI!	 r   c                6    t                ddlm}  || |      S )aA  Build a Kornia ``Normalize`` transform for GPU-side normalization.

    Args:
        mean: Per-channel mean values.  Defaults to ImageNet statistics.
        std: Per-channel standard deviation values.  Defaults to ImageNet
            statistics.

    Returns:
        A ``kornia.augmentation.Normalize`` instance.
    r   )	Normalize)meanrb   )r   r   r   )r   rb   r   s      r   build_normalizer   W  s      - r   c                   t        |       dk(  r@t        j                  ddd|      t        j                  ddt        j                  |      fS | D cg c]  }|d   j                  d    }}|rt        |      nd}t        |       }|dk(  r@t        j                  |dd|      t        j                  |dt        j                  |      fS t        j                  ||d|      }t        j                  ||t        j                  |      }t        |       D ]2  \  }}|d   j                  d   }	|	dkD  s|d   ||d|	f<   d||d|	f<   4 ||fS c c}w )u  Pack variable-length xyxy boxes into a padded tensor and valid mask.

    Kornia ``AugmentationSequential`` expects boxes as ``[B, N_max, 4]``. This function zero-pads each image's boxes to
    the maximum count in the batch and returns a boolean mask indicating which entries are real.

    Args:
        targets: List of target dicts (one per image), each containing a
            ``"boxes"`` key with an ``[N_i, 4]`` tensor in xyxy format.
        device: Device on which to allocate the output tensors.

    Returns:
        Tuple of:
            - ``boxes_padded`` — ``[B, N_max, 4]`` float tensor (zero-padded).
            - ``valid_mask``   — ``[B, N_max]`` bool tensor (``True`` = real box).

        When ``B == 0`` or all images have zero boxes, both tensors have ``N_max == 0``.
    r      )devicedtyper   boxesNT)rE   torchzerosboolshaperF   	enumerate)
targetsr   rH   
box_countsn_max
batch_sizeboxes_padded
valid_maskins
             r   collate_boxesr   s  sP   * 7|qKK1a/KK1EJJv>
 	

 077!!G*""1%7J7)C
OqEWJzKK
Aq8KK
AUZZG
 	

 ;;z5!FCLZejjPJ'" %1gJQq5"#G*LBQB $Jq"1"u	% ##' 8s   Ec                F   t        |       }t        j                  ||||t        j                  |      }t	        |       D ]^  \  }}d|vs|dk(  r|d   j                  t        j                  |      }	t        |	j                  d   |      }
|
dkD  sS|	d|
 ||d|
f<   ` |S )u  Pack variable-length instance masks into a zero-padded ``[B, N_max, H, W]`` tensor.

    Kornia ``AugmentationSequential`` expects masks as ``[B, N_max, H, W]`` when ``data_keys`` includes ``"mask"``.
    This function zero-pads each image's masks to *n_max* channels (matching the padding used by :func:`collate_boxes`)
    and converts boolean masks to ``float32`` for Kornia compatibility.

    Args:
        targets: List of target dicts (one per image).  Each dict may optionally
            contain a ``"masks"`` key with an ``[N_i, H, W]`` boolean tensor. Dicts without the key are treated as
            having zero instances.
        device: Device on which to allocate the output tensor.
        n_max: Maximum instance count across the batch — must equal
            ``collate_boxes(targets, device)[1].shape[1]`` to keep box/mask indices in sync.
        image_height: Spatial height ``H`` of each mask (pixels).
        image_width: Spatial width ``W`` of each mask (pixels).

    Returns:
        Float32 tensor of shape ``[B, N_max, H, W]``, zero-padded where ``N_i < N_max``.  Boolean input masks are cast
        to ``float32`` (``True → 1.0``, ``False → 0.0``).

    Examples:
        >>> import torch
        >>> targets = [{"masks": torch.ones(2, 8, 8, dtype=torch.bool)}]
        >>> out = collate_masks(targets, torch.device("cpu"), n_max=2, image_height=8, image_width=8)
        >>> out.shape
        torch.Size([1, 2, 8, 8])
        >>> out.dtype
        torch.float32
    r   masksr   N)rE   r   r   float32r   tominr   )r   r   r   image_heightimage_widthr   masks_paddedr   rH   masks_ir   s              r   collate_masksr     s    H WJ;;z5,SXS`S`iopL'" .1!uzG*--emmF-Ca %(q5")"1+LBQB. r   c                   |S|j                   dd |j                   k(  s7J dt        |j                   dd        dt        |j                          d       g }t        |      D ]  \  }}|j                         }|d   j                   d   }	|	dk(  s|j                   d   dk(  r|j	                  |       R||d|	f   }
| |d|	f   }|j                         }|dddf   j                  d|	       |dddf   j                  d|	       |dddf   j                  d|	       |ddd
f   j                  d|	       |dddf   |dddf   z
  }|ddd
f   |dddf   z
  }|
|dkD  z  |dkD  z  }||   |d<   d|v r|d   |   |d<   d|v r/|d   }|dddf   |dddf   z
  |ddd
f   |dddf   z
  z  |d<   d|v r|d   |   |d<   |||d|	f   }||   t        kD  |d<   |j	                  |        |S )ue  Unpack augmented boxes (and optionally masks), clamp to image bounds, remove zero-area boxes.

    After Kornia augmentation the padded ``[B, N_max, 4]`` tensor is unpacked back into per-image target dicts.  Boxes
    are clamped to ``[0, W] x [0, H]`` and any that collapse to zero area are removed along with their corresponding
    ``labels``, ``area``, ``iscrowd``, and (if provided) ``masks`` entries.

    Args:
        boxes_aug: Augmented boxes tensor ``[B, N_max, 4]`` in xyxy format.
        valid: Boolean mask ``[B, N_max]`` from :func:`collate_boxes`.
        targets: Original target dicts; each dict is shallow-copied before
            modification — the input list itself is not mutated.
        image_height: Image height in pixels (for clamping).
        image_width: Image width in pixels (for clamping).
        masks_aug: Optional augmented masks tensor ``[B, N_max, H, W]``
            (float32) from Kornia.  When provided, masks are filtered by the same ``keep`` mask as boxes, thresholded at
            ``> 0.5`` to bool, and stored under ``"masks"`` in each output target dict.  When ``None``, any existing
            ``"masks"`` entry in the target dict is preserved unchanged.

    Returns:
        A new list of target dicts with updated ``boxes``, ``labels``, ``area``, ``iscrowd``, and (when *masks_aug* is
        given) ``masks`` entries.
    Nr?   zmasks_aug batch/n_max dims z must match valid shape zM; ensure collate_masks is called with n_max=valid.shape[1] from collate_boxesr   r   r@   )r   rF   r[   labelsareaiscrowdr   )r   r7   r   copyrz   cloneclamp_r   )	boxes_augvalidr   r   r   	masks_augnew_targetsr   rH   n_origvboxes_iwidthsheightskeep
kept_boxesr   s                    r   unpack_boxesr     s   < r"ekk1 	
)%	0C*D)E F -. /66	
1
 )+K'" (1FFH7!!!$Q;%++a.A-q! !WfW*AwwJ' --/14151415 AA.!Q$-'!Q$-/FQJ7Q;/T]'
q=H+d+AhKQ;7J#AqD)Jq!t,<<AqDAQT^_`bc_cTdAdeAfI>Y<-AiL 7F7
+G )AAAgJ
 	1Q(T r   )returnr   )r!   r   r   r   )r   None)r*   zdict[str, Any]r   r   )FF)
r{   zdict[str, dict[str, Any]]r|   intr}   r   ro   r   r   r   )r   tuple[float, ...]rb   r   r   r   )r   list[dict[str, Any]]r   torch.devicer   ztuple[Tensor, Tensor])r   r   r   r   r   r   r   r   r   r   r   r   )N)r   r   r   r   r   r   r   r   r   r   r   zTensor | Noner   r   )$__doc__
__future__r   collections.abcr   typingr   r   r   rfdetr.datasets._aug_utilsr   rfdetr.utilities.loggerr   rv   __doctest_requires__IMAGENET_MEANIMAGENET_STDr   __annotations__r   r"   r   r+   r.   r;   rI   rS   rW   r^   rc   rl   r	   r   r   r   r    r   r   <module>r      s  !F # $    J .	/(<  &$ #& % %*$$eN	q"86*8"& ,'% @'#	9	5 	( #	9)99 9 	9
 	9z ,)
	 	8.$!.$.$ .$b-!-- - 	-
 - -l  $OOO "O 	O
 O O Or   