
    ^jW                    B   d dl mZ d dlZd dlmZ d dlmZ d dlmZm	Z	 d dl
Zd dlZer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mZ ddgZddgZddgZh dZ d/dZ!d0dZ"d/dZ#d1dZ$d2dZ%d3dZ&d4dZ' ed       G d d             Z( G d d      Z)dd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d5d Z*	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d6d!Z+d"dd#	 	 	 	 	 	 	 	 	 	 	 	 	 d7d$Z,d8d%Z-d8d&Z.	 	 	 	 	 	 	 	 	 	 d9d'Z/	 d:	 	 	 	 	 	 	 d;d(Z0d<d)Z1 G d* d+      Z2 G d, d-e      Z3d=d.Z4y)>    )annotationsN)	dataclass)Path)TYPE_CHECKINGAny
Detections)Image	ImageDraw)VisionDataset)YoloKeypointSchema#_extract_yolo_class_names_from_data_load_yaml_mappinginfer_yolo_keypoint_schema)%_resolve_runtime_augmentation_backendmake_coco_transforms"make_coco_transforms_square_div_64	data.yamlzdata.ymltrainvalidimageslabels>	   .bmp.dng.jpg.mpo.png.tif.jpeg.tiff.webpc           	     "   | \  }}}}t        j                  t        |      t        |      dz  z
  t        |      t        |      dz  z
  t        |      t        |      dz  z   t        |      t        |      dz  z   gt         j                        S )zDParse a YOLO center-width-height box into relative XYXY coordinates.   dtype)nparrayfloatfloat32)valuesx_centery_centerwidthheights        _/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/datasets/yolo.py_parse_yolo_boxr0   (   s    (.%Hhv88(OeElQ..(OeFma//(OeElQ..(OeFma//		
 jj     c                    t        j                  | d   | d   g| d   | d   g| d   | d   g| d   | d   ggt         j                        S )z4Convert a relative XYXY box into a 4-corner polygon.r      r#      r$   )r&   r'   r)   )boxs    r/   _box_to_polygonr6   6   sZ    88
a&#a&	CFCF+c!fc!f-=AA?OPjj r1   c                l    t        j                  | t         j                        j                  dd      S )z7Parse a flattened YOLO polygon into relative XY points.r$   r#   )r&   r'   r)   reshape)r*   s    r/   _parse_yolo_polygonr:   >   s$    88F"**-55b!<<r1   c                2   |\  }}t        j                  d||fd      }| j                  dkD  rKt        j                  |      j                  | j                         D cg c]  }t        |       c}d       t        j                  |t              S c c}w )zRasterize a polygon into a dense boolean mask.

    TODO: remove once supervision ships a direct CompactMask.from_polygon factory;
    at that point the dense intermediate array is no longer needed.
    Lr   r3   )fillr$   )r
   newsizer   Drawpolygontolisttupler&   r'   bool)rA   resolution_whr-   r.   maskpoints         r/   _polygon_to_maskrH   C   sy     "ME699S5&/1-D||at$$@P%QueEl%QXY$Z88D%% &Rs   Bc           	         t        |       dk(  r#|\  }}t        j                  d||ft              S t        j                  | D cg c]  }t        ||       c}      S c c}w )zRasterize per-instance polygons into an ``(N, H, W)`` boolean array.

    TODO: remove once supervision ships a direct CompactMask.from_polygon factory;
    at that point the dense intermediate array is no longer needed.
    r   r$   )lenr&   zerosrD   stackrH   )polygonsrE   r-   r.   rA   s        r/   _polygons_to_masksrN   P   sV     8}%vxxFE*$7788XV'%g}=VWWVs   Ac                T    t        d t        |       j                         D              S )z(List YOLO image files in a stable order.c              3     K   | ]?  }|j                         r-|j                  j                         t        v rt	        |       A y wN)is_filesuffixlowerYOLO_IMAGE_EXTENSIONSstr).0paths     r/   	<genexpr>z)_list_yolo_image_paths.<locals>.<genexpr>^   s:      <<>dkk//15JJ 	D	s   AA)sortedr   iterdir)images_directory_paths    r/   _list_yolo_image_pathsr]   \   s+     ./779  r1   c                F    t        |       }t        |      }t        ||      S )z0Read class names from a YOLO ``data.yaml`` file.)r   r   r   )	data_filerX   datas      r/   _extract_yolo_class_namesra   e   s"    	?Dd#D.tT::r1   T)frozenc                  `    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   d
ed<   ded<   ddZy)_LazyYoloSampleaq  Lightweight per-image YOLO metadata with polygons kept lazy until fetch time.

    Note: ``frozen=True`` prevents field *reassignment* but does NOT prevent
    in-place mutation of ``np.ndarray`` fields (e.g. ``sample.xyxy[0] = 999.0`` would silently succeed).  This is safe
    across DataLoader workers because each worker receives a pickled copy of the dataset.
    rV   
image_pathintr-   r.   
np.ndarrayxyxyclass_idtuple[np.ndarray, ...]rM   	keypointsc                b   ddl m} t        | j                        dk(  r |j                         S t        | j
                        dk(  r || j                  | j                        S t        | j
                  | j                  | j                  f      } || j                  | j                  |      S )zFMaterialize the current sample as a supervision ``Detections`` object.r   r   )ri   rh   )ri   rh   rF   )
supervisionr	   rJ   ri   emptyrM   rh   rN   r-   r.   )selfr	   rF   s      r/   to_detectionsz_LazyYoloSample.to_detections}   s    *t}}"#:##%%t}}"t}}499EE "$--$**dkk1JK4==tyytLLr1   N)returnz'Detections')__name__
__module____qualname____doc____annotations__rp    r1   r/   rd   rd   l   s6     OJK
$$Mr1   rd   c                  0    e Zd ZdZddZddZd	dZd
dZy)_LazyYoloDetectionDatasetzMLazy YOLO dataset that defers dense mask rasterization until ``__getitem__``.c                     || _         || _        y rQ   )classes_samples)ro   r{   sampless      r/   __init__z"_LazyYoloDetectionDataset.__init__   s    r1   c                ,    t        | j                        S rQ   )rJ   r|   ro   s    r/   __len__z!_LazyYoloDetectionDataset.__len__   s    4==!!r1   c                   | j                   |   }	 t        j                  |j                        5 }t	        j
                  |j                  d            }d d d        |j                  |j                         fS # 1 sw Y   &xY w# t        t        t        j                  f$ r}t        d|j                         |d }~ww xY w)NRGBz Could not read image from path: )r|   r
   openre   r&   r'   convertFileNotFoundErrorOSErrorUnidentifiedImageError
ValueErrorrp   )ro   idxsampleimage	rgb_imageexcs         r/   __getitem__z%_LazyYoloDetectionDataset.__getitem__   s    s#	^F--. ;%HHU]]5%9:	;   )V-A-A-CCC	; ;!7E,H,HI 	^?@Q@Q?RSTZ]]	^s.   B %A:B :B?B C$B==Cc                     | j                   |   S )zBReturn lightweight metadata without loading pixels or dense masks.)r|   )ro   r   s     r/   get_image_infoz(_LazyYoloDetectionDataset.get_image_info   s    }}S!!r1   N)r{   	list[str]r}   zlist[_LazyYoloSample]rq   Nonerq   rf   )r   rf   rq   z$tuple[str, np.ndarray, 'Detections'])r   rf   rq   rd   )rr   rs   rt   ru   r~   r   r   r   rw   r1   r/   ry   ry      s    W "D"r1   ry   parse_polygonsc                  t        |       dk  r't        dt        |      d| dt        |        d      t        |       dkD  r>t        | dd       dz  d	k7  r*t        d
t        |      d| dt        | dd        d      	 t        | d	         }|d	k  s||k\  r't        dt        |      d| d| d| d|dz
   d      t        |       dk(  rt	        | dd       }	|rt        |	      nd}
n	 t        | dd       }t        j                  t        j                  |ddd	f         t        j                  |dddf         t        j                  |ddd	f         t        j                  |dddf         gt        j                        }	|r|nd}
|	t        j                  ||||gt        j                        z  }|
||dfS |
t        j                  ||gt        j                        z  }t        j                  |ddd	f   dt        |dz
              |ddd	f<   t        j                  |dddf   dt        |dz
              |dddf<   |||j                  t        j                        fS # t        $ r'}t        dt        |      d| d| d	   d      |d}~ww xY w# t        $ r!}t        d
t        |      d| d      |d}~ww xY w)a  Parse one YOLO label line and return ``(class_id, xyxy_px, polygon_px)``.

    Args:
        values: Whitespace-split fields from the label line.
        line_num: 1-based line number (for error messages).
        label_path: Path to the label file (for error messages).
        num_classes: Total number of classes in the dataset (used for range check).
        width: Image width in pixels.
        height: Image height in pixels.
        parse_polygons: When ``False`` the pixel-space polygon array is not
            computed or returned (``polygon_px`` will be ``None``).  Set to ``False`` on the detection-only path to
            avoid allocating polygon arrays that would immediately be discarded.

    Returns:
        Tuple of ``(class_id, xyxy_px, polygon_px)`` where coordinates are in pixel space.  ``polygon_px`` is ``None``
        when ``parse_polygons=False``.

    Raises:
        ValueError: If the line is malformed or the class ID is out of range.
       zMalformed label in 	 at line uc   : expected 5 (bbox) fields or ≥ 7 fields for polygons (class_id + at least 3 (x, y) points), got .r3   Nr#   r   zMalformed polygon in z<: polygon coordinates must be paired (x, y) values, but got z coordinate values (odd count).zLabel z line z: invalid class ID z (must be an integer).z: class ID z" is out of range for dataset with u    classes (valid range 0–z).z.: could not parse coordinate values as floats.r$           )rJ   r   rV   rf   r0   r6   r:   r&   r'   minmaxr)   clipr(   astype)r*   line_num
label_pathnum_classesr-   r.   r   cidr   r5   rA   _raw_polygonxyxy_px
polygon_pxs                 r/   _parse_yolo_label_liner      s   < 6{Q!#j/!4IhZ H::=f+aI
 	

 6{Q3vabz?Q.!3#C
O#6iz J6!":''FH
 	

&)n Qw#$S_'vhZ 8u>{m L##.?"327
 	

 6{afQRj)=K_S%9QU	.vabz:L hh|AqD)*|AqD)*|AqD)*|AqD)*	 **
 #1,dBHHeVUF;2::NNGGT!!288UFO2::FFJwwz!Q$/eEAI6FGJq!twwz!Q$/eFQJ6GHJq!t**2::666Y  S_'vhZ7J6RS9-Wmn
	*  	'J':)H: N? @ 	s0   I5 /J( 5	J%>"J  J%(	K1KKc                  d||z  z   }t        |       |k7  rIt        |       dk(  r|dkD  rdnd}	t        dt        |      d| d| d| d	| d
t        |        d|	       t        | dd |||||d      \  }
}}	 t	        j
                  | dd t        j                        j                  ||      }t	        j                  |      j                         st        dt        |      d| d      |ddddf   }t	        j                  |dft        j                        }|dk(  re|dddf   }t	        j                  |dk  |dkD  z        rt        dt        |      d| d      t	        j                  |dd|       ||dddf<   n\|dddf   dk  |dddf   dk  z  }t	        j                  |dd|       d||ddf<   |dddf   dk(  |dddf   dk(  z   }d||df<   |dddf   t        |      z  |dddf<   |dddf   t        |      z  |dddf<   |dddf   dk  }d||ddf<   |
||fS # t        $ r!}t        dt        |      d| d      |d}~ww xY w)zNParse one Ultralytics YOLO pose row into pixel boxes and COCO-style keypoints.r   r   z This looks like a detection-only label row (5 fields). Check whether the dataset mixes detection and pose annotations or whether the kpt_shape in data.yaml is correct. zMalformed YOLO pose label in r   z: expected z fields from kpt_shape=[z, z], got r   NFr   r$   z,: could not parse keypoint values as floats.z: non-finite keypoint.r#   r4   r   g       @z/: keypoint visibility values must be in [0, 2].g      ?)outr3   )rJ   r   rV   r   r&   asarrayr)   r9   isfiniteallrK   anyr   r(   )r*   r   r   r   r-   r.   num_keypointskeypoint_dimexpected_fieldshintr   r   _raw_keypointsr   xyrk   
visibility	absent_2dpresentabsents                        r/   _parse_yolo_pose_label_liner     s    -,66O
6{o%
 6{aMA$5@  	 +C
O+>iz R'((@rR^Q_ `v;-q(
 	
 -r
OC!

6!":RZZ@HHXde ;;}%))+8Z8K9U]T^^tuvv	q"1"u	B-+2::>Iq"1a4(
66:#
S(89:/J/B)H: V@ @  	C"%$	!Q$ 1X^1a437	
C"%9a<1X_AqDS9: #	'1*Ahu-IadOAhv.IadOq!t_#FIfbqbj""M  +C
O+>iz R9 9
 	s   :8H: :	I$II$F)include_keypointskeypoint_schemac                  |r|rt        d      |r?|xs t        |      }|j                  }t        |j                  d      }|j
                  }nt        |      }d}d}g }	t        |       D ]I  }
t        |      t        |
      j                   dz  }t        j                  |
      5 }|j                  \  }}ddd       g }g }g }g }|j                         r |j                  d      5 }|D cg c]#  }|j                         s|j                         % }}ddd       t              D ]  \  }}|j!                         }|r7t#        ||dz   |t%        |      ||	      \  }}}d}|j'                  |       n"t)        ||dz   |t%        |      |
      \  }}}|j'                  |       |j'                  |       |s||j'                  |        |	j'                  t+        |
t-        j.                  |t,        j0                        j3                  dd      t-        j.                  |t,        j4                        t7        |      |r4t-        j8                  |      j;                  t,        j0                  d      n't-        j<                  d|dft,        j0                                     L ||	fS # 1 sw Y   xY wc c}w # 1 sw Y   xY w)u>  Build the class list and sample list shared by both YOLO builder functions.

    Iterates over every image in ``img_folder``, reads image dimensions via PIL (header-only, no full decode), and
    parses the matching ``.txt`` label file when present.  Images without a label file are included as *background*
    samples with empty detections.

    Args:
        img_folder: Path to the directory containing images.
        lb_folder: Path to the directory containing YOLO ``.txt`` label files.
        data_file: Path to the ``data.yaml`` / ``data.yml`` file with class names.
        include_polygons: When ``True`` polygon coordinates are stored in each
            :class:`_LazyYoloSample` (segmentation path).  When ``False`` polygon coordinates returned by
            :func:`_parse_yolo_label_line` are discarded and ``polygons=()`` is stored instead (detection-only path).
            Mutually exclusive with ``include_keypoints``.
        include_keypoints: When ``True`` keypoint coordinates are stored in each :class:`_LazyYoloSample` (pose path).
            Mutually exclusive with ``include_polygons``; raises :class:`ValueError` when both are ``True``.
        keypoint_schema: Keypoint schema describing class names, per-class keypoint counts, OKS sigmas, keypoint names,
            flip index, and keypoint dimensionality.  When ``None`` and ``include_keypoints=True`` the schema is
            auto-inferred from ``data_file`` via :func:`infer_yolo_keypoint_schema`.

    Returns:
        A ``(classes, samples)`` tuple where ``classes`` is the ordered list of class names and ``samples`` is a list of
        :class:`_LazyYoloSample` objects.

    Examples:
        >>> # Used internally by _build_lazy_yolo_detection_dataset and
        >>> # _build_lazy_yolo_segmentation_dataset — not part of the public API.
        >>> pass
    HYOLO segmentation masks and keypoints cannot be loaded at the same time.r   defaultz.txtNzutf-8)encodingr3   )r   r   r   r$   r8      F)copyr4   )re   r-   r.   rh   ri   rM   rk   )r   r   class_namesr   num_keypoints_per_classr   ra   r]   r   stemr
   r   r?   existsstrip	enumeratesplitr   rJ   appendr   rd   r&   r'   r)   r9   int64rC   rL   r   rK   )
img_folder	lb_folderr_   include_polygonsr   r   r{   r   r   r}   re   r   r   r-   r.   rh   ri   rM   rk   handlelinelinesir*   r   r   keypoints_pxr   s                               r/   _build_yolo_samplesr   S  s   L -cdd)R-G	-R!--OCCQO&33+I6%'G,Z8 8

)_$z*:*?*?)@'EE
ZZ
# 	'u!JJME6	' "$ %'&(	'2 Jf28I$DJJLIIJ$U+ 04$1LA"G&3%1	2.C, "&J$$\2/EA"G'70,C* $G$#
(>OOJ/90< 	%XXd"**5==b!D("((;x ! HHY'..rzz.F1mQ"7rzzJ	
U8
t Gq	' 	' JJ Js0   *J>-K2KKK>K	KK	c                @    t        | ||d      \  }}t        ||      S )u~  Build a YOLO detection dataset that stores bounding boxes lazily.

    Unlike :func:`_build_lazy_yolo_segmentation_dataset`, this function does not store polygon coordinates or dense
    masks — only ``xyxy`` boxes are retained, keeping peak memory proportional to the number of annotations.

    Images without a matching ``.txt`` label file are included as *background* samples with empty detections, so
    datasets that mix labelled and unlabelled images are handled correctly.

    Args:
        img_folder: Path to the directory containing images.
        lb_folder: Path to the directory containing YOLO ``.txt`` label files.
        data_file: Path to the ``data.yaml`` / ``data.yml`` file with class names.

    Returns:
        A :class:`_LazyYoloDetectionDataset` whose ``__getitem__`` loads pixel data on demand and returns
        ``sv.Detections`` without mask information.
    Fr   r{   r}   r   ry   r   r   r_   r{   r}   s        r/   "_build_lazy_yolo_detection_datasetr     s'    $ +:y)^cdGW$WgFFr1   c                @    t        | ||d      \  }}t        ||      S )a  Build a YOLO dataset that stores polygons and rasterizes masks on demand.

    Args:
        img_folder: Path to the directory containing images.
        lb_folder: Path to the directory containing YOLO ``.txt`` label files.
        data_file: Path to the ``data.yaml`` / ``data.yml`` file with class names.

    Returns:
        A :class:`_LazyYoloDetectionDataset` whose ``__getitem__`` loads pixel data on demand and rasterizes polygon
        masks into dense boolean tensors.
    Tr   r   r   r   s        r/   %_build_lazy_yolo_segmentation_datasetr     s'     +:y)^bcGW$WgFFr1   c                D    t        | ||dd|      \  }}t        ||      S )zDBuild a YOLO pose dataset that stores keypoints without dense masks.FT)r   r   r   r   r   )r   r   r_   r   r{   r}   s         r/   !_build_lazy_yolo_keypoint_datasetr     s4     +'GW %WgFFr1   c           
        ddl m} g }g }g }t        |       D ];  \  }}||dd}	|t        |j                        |	d<   g |	d<   |j                  |	       = t        |d      }
d}t        t        |            D ]N  }|
rr|j                  |      }|j                  }|j                  |j                  }}|j                  }|j                  }t        |j                        dkD  }|j                   }nr||   \  }}}|j"                  dd	 \  }}|j                  }|j                  }|j$                  du}t'        j(                  t        |      dd
ft&        j*                        }|j                  |t-        |      t/        |      t/        |      d       t        t        |            D ]  }||   \  }}}}t1        |      t1        |      }}t1        ||z
        t1        ||z
        }}||t/        ||         ||||gt1        ||z        dd} |rg | d<   ||t        |      k  r||   n$t'        j(                  dt&        j*                        }!|!j3                  d      j5                  t0              j7                         | d<   t/        t'        j8                  |!ddd	f   dkD              | d<    |j
                  |        |dz  } Q ddi|||d}" |       }#|"|#_        |#j=                          |#S )am  Build an in-memory ``pycocotools.COCO`` object from YOLO lazy samples.

    Args:
        classes: Ordered class names where index is the YOLO class ID.
        dataset: Lazy YOLO backend exposing ``__len__`` and either ``get_image_info(idx)`` or ``__getitem__(idx)``.

    Returns:
        Initialized ``pycocotools.COCO`` object with ``dataset`` and indexes.
    r   )COCOnone)idnamesupercategoryNrk   skeletonr   r#   r4   r$   )r   	file_namer.   r-   )r   image_idcategory_idbboxareaiscrowdsegmentation)r   r4   r8   r   r3   descriptionzRF-DETR YOLO dataset)infor   r   
categories)pycocotools.cocor   r   listkeypoint_namesr   hasattrrangerJ   r   re   r.   r-   rh   ri   rM   rk   shaperF   r&   rK   r)   rV   rf   r(   r9   r   rB   count_nonzerodatasetcreateIndex)$r{   r   r   r   r   r   r   r   
class_namecategoryuse_lazy_pathann_idimg_idr   re   r.   r-   rh   ri   	has_masksrk   image_array
detectionsr   x1y1x2y2bbox_xbbox_ybbox_wbbox_hannkeypoints_icoco_datasetcocos$                                       r/   _build_coco_api_from_samplesr    s    &#%F(*K')J$W- $ZzFK&$()G)G$HH[!#%HZ (#$ G%56MFG% '++F3F**J"MM6<<EF;;DHFOO,q0I((I29&//JZ'--bq1MFE??D!**H"t3I#d)Q!2"**EIV#j/SQW[cfglcmnos4y! 	A!!WNBB"2Yb	FF"27^U27^FF""8A;/8fvo.C &(N#*./#i..@ilbhhv]_]g]gFh#.#6#6r#:#A#A%#H#O#O#QK '*2+;+;K1<MPQ<Q+R'SO$Ks#aKF)	''T  67" 	L 6DDLKr1   c                     t         fdt        D              }t         fdt        D              }t         fdt        D              }|xr |xr |S )a  Checks if the specified dataset directory is in yolo format.

    We accept a dataset to be in yolo format if the following conditions are met:
    - The dataset_dir contains a data.yaml or data.yml file
    - The dataset_dir contains "train" and "valid" subdirectories, each containing "images" and "labels" subdirectories
    - The "test" subdirectory is optional

    Returns a boolean indicating whether the dataset is in correct yolo format.
    c              3     K   | ]A  }t         j                  j                  t         j                  j                  |             C y wrQ   osrX   r   join)rW   	yaml_filedataset_dirs     r/   rY   z(is_valid_yolo_dataset.<locals>.<genexpr>V  s2      &AJrww||K;<&   AA
c              3     K   | ]A  }t         j                  j                  t         j                  j                  |             C y wrQ   r  )rW   	split_dirr  s     r/   rY   z(is_valid_yolo_dataset.<locals>.<genexpr>Y  s2      'AJrww||K;<'r  c           	   3     K   | ]M  }t         D ]B  }t        j                  j                  t        j                  j	                  ||             D O y wrQ   )REQUIRED_DATA_SUBDIRSr  rX   r   r  )rW   r  data_subdirr  s      r/   rY   z(is_valid_yolo_dataset.<locals>.<genexpr>\  sL      )0)  	rww||KKHI)I)s   AA)r   REQUIRED_YOLO_YAML_FILESr   REQUIRED_SPLIT_DIRS)r  contains_required_yolo_yamlcontains_required_split_dirscontains_required_data_subdirss   `   r/   is_valid_yolo_datasetr!  L  se     #& &Nf& # $' 'Na' $  &) ),) &"
 'j+GjLjjr1   c                  "    e Zd ZdZdddZddZy)ConvertYoloa  Converts supervision Detections to the target dict format expected by RF-DETR.

    Args:
        include_masks: whether to include segmentation masks.
        include_keypoints: whether to include pose keypoints.
        num_keypoints: Number of keypoints per instance when keypoints are enabled.

    Examples:
        >>> import numpy as np
        >>> from supervision import Detections
        >>> from PIL import Image
        >>> # Create a sample image and target
        >>> image = Image.new("RGB", (100, 100))
        >>> detections = Detections(
        ...     xyxy=np.array([[10, 20, 30, 40]]),
        ...     class_id=np.array([0])
        ... )
        >>> target = {"image_id": 0, "detections": detections}
        >>> # Create converter
        >>> converter = ConvertYolo(include_masks=False)
        >>> # Call converter
        >>> img, result = converter(image, target)
        >>> sorted(result.keys())
        ['area', 'boxes', 'image_id', 'iscrowd', 'labels', 'orig_size', 'size']
        >>> result["boxes"].shape
        torch.Size([1, 4])
        >>> result["labels"].tolist()
        [0]
        >>> result["image_id"].tolist()
        [0]
    c                .    || _         || _        || _        y rQ   include_masksr   r   )ro   r&  r   r   s       r/   r~   zConvertYolo.__init__  s    *!2*r1   c                   |j                   \  }}|d   }t        j                  |g      }|d   }t        |      dkD  ryt        j                  |j
                        j                  t        j                        }t        j                  |j                        j                  t        j                        }nJt        j                  dt        j                        }t        j                  dt        j                        }|dddddf   j                  d|	       |ddd
ddf   j                  d|	       |dddf   |ddd
f   kD  |dddf   |dddf   kD  z  }	||	   }||	   }i }
||
d<   ||
d<   ||
d<   |dddf   |ddd
f   z
  |dddf   |dddf   z
  z  }||
d<   t        j                  |j                  d   ft        j                        }||
d<   | j                  r|j                  t        j                   |j                        dkD  rat        j                  |j                  |	j!                         j#                                  j                  t        j$                        }||
d<   n+t        j                  d||ft        j$                        |
d<   |
d   j'                         |
d<   | j(                  r|j+                  d      }|@t        j                  |	j                  d   | j,                  dft        j                        }n@t        j.                  |t        j                        j1                  d| j,                  d      }||	   |
d<   t        j.                  t3        |      t3        |      g      |
d<   t        j.                  t3        |      t3        |      g      |
d<   ||
fS )zConvert image and YOLO detections to RF-DETR format.

        Args:
            image: PIL Image
            target: dict with 'image_id' and 'detections'

        Returns:
            tuple of (image, target_dict)
        r   r  r   )r   r   r$   )r   Nr#   )r   r   r3   r4   boxesr   r   r   masksrk   r8   	orig_sizer?   )r?   torchtensorrJ   
from_numpyrh   tor)   ri   r   rK   clamp_r   r&  rF   r&   cpunumpyuint8rD   r   getr   	as_tensorr9   rf   )ro   r   targetwhr   r  r(  r{   keep
target_outr   r   r)  r   rk   s                   r/   __call__zConvertYolo.__call__  s8    zz1*%<<
+L)
z?Q$$Z__588GE&&z':':;>>u{{KGKKemm<Ekk$ekk:G 	aAg!+aAg!+adeAqDk)eAqDkE!Q$K.GHd$-
#
7&
8!)
: adeAqDk)eAqDkE!Q$K.GH!
6++w}}Q/1E '
9*rwwz/G!/K((9I9I9K)LMPPQVQ\Q\]&+
7#&+kk1a)5;;&O
7#",W"5":":"<Jw!!"JJ{3M$!KKA8J8JA(NV[VcVcd	!OOMOWWXZ\`\n\npqr	&/oJ{#"'//3q63q62B"C
;"__c!fc!f-=>
6j  r1   N)FFr   )r&  rD   r   rD   r   rf   )r   zImage.Imager5  dictrq   rC   )rr   rs   rt   ru   r~   r:  rw   r1   r/   r#  r#  d  s    @+
A!r1   r#  c                  T     e Zd ZdZ	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 d fdZddZddZ xZS )	YoloDetectionu  YOLO format dataset with lazy image loading and optional mask support.

    Both detection (``include_masks=False``) and segmentation (``include_masks=True``) paths use a lazy backend: image
    pixels are loaded on demand inside ``__getitem__`` rather than at construction time, which keeps peak RAM
    proportional to the number of annotations rather than to ``N × H × W``.

    Images without a matching ``.txt`` label file are treated as *background* images and produce empty detections.  This
    ensures that datasets containing a mix of annotated and unannotated images are handled correctly in both single-GPU
    and multi-GPU training.

    This class provides a VisionDataset interface compatible with RF-DETR training, matching the API of CocoDetection.

    Args:
        img_folder: Path to the directory containing images
        lb_folder: Path to the directory containing YOLO annotation .txt files
        data_file: Path to data.yaml file containing class names and dataset info
        transforms: Optional transforms to apply to images and targets
        include_masks: Whether to load segmentation masks (for YOLO segmentation format).
            When True polygons are parsed and rasterized on demand; when False only bounding-box coordinates are stored.
        include_keypoints: Whether to load Ultralytics YOLO pose keypoints.
        num_keypoints_per_class: Optional keypoint schema used by RF-DETR.
    c                \   |r|rt        d      t        t        |   |       || _        || _        || _        |r	 t        |      | _        nd | _        t        |xs g d      | _        | j                  0t        | j                  j                  | j                        | _        t        ||| j                        | _        |rt!        |||| j                        | _        n'|rt%        |||      | _        nt'        |||      | _        | j"                  j(                  | _        t+        t-        t/        | j"                                    | _        t3        | j(                  | j"                  | j                        | _        y # t        t         t        f$ r}t        d|d      |d }~ww xY w)Nr   z6YOLO keypoint training requires kpt_shape metadata in r   r   r   r%  )r   superr=  r~   _transformsr&  r   r   r   r   r   r   r   r   r#  preparer   
sv_datasetr   r   r{   r   r   rJ   idsr  r  )
ro   r   r   r_   
transformsr&  r   r   r   	__class__s
            r/   r~   zYoloDetection.__init__  s    .ghhmT+J7%*!2s'A)'L$ $(D  !8!>BJ+!$T%9%9%Q%Q[_[m[m!nD"'/,,

 ?$$	DO CJPY[deDO@YXabDO..c$//234 1tPTPdPde	9 &z7; s #YZcYffg!hiorrss   F F+F&&F+c                ,    t        | j                        S rQ   )rJ   rB  r   s    r/   r   zYoloDetection.__len__  s    4??##r1   c                V   | j                   |   }| j                  |   \  }}}t        j                  |      }||d}| j                  r(| j                  j                  |      j                  |d<   | j                  ||      \  }}| j                  | j                  ||      \  }}||fS )N)r   r  rk   )	rC  rB  r
   	fromarrayr   r   rk   rA  r@  )ro   r   r   re   r   r  imgr5  s           r/   r   zYoloDetection.__getitem__  s    88C=,0OOC,@)
Izooi(&jA!!"&//"@"@"E"O"OF;ll3/V'**37KCF{r1   )NFFN)r   rV   r   rV   r_   rV   r&  rD   r   rD   r   zlist[int] | Noner   )r   rf   )rr   rs   rt   ru   r~   r   r   __classcell__)rE  s   @r/   r=  r=    sg    8 #"'48/f/f /f 	/f /f  /f "2/fb$r1   r=  c                   t        |j                        j                         sJ d d       dz  dz  dz  dz  fdz  dz  dz  dz  fdz  dz  dz  dz  fd}t        fd	t        D        d
z        }|| j                  d      d      \  }}t        |dd      }t        |dd      }t        |dd      }	t        |dd      }
t        |dd      }t        |dd      }t        |dd      }t        |dd      }t        |dd      }t        |dg       }|rt        |dg       xs g nd}t        t        |dd            }|dk7  }|r	 t        |       |rCt        t        |      t        |      t        |      t        | ||	|
| |||||
      |||      }|S t        t        |      t        |      t        |      t        | ||	|
| |||||
      |||      }|S # t        t        t        f$ r}t        d      |d}~ww xY w)a  Build a Roboflow YOLO-format dataset.

    This uses Roboflow's standard YOLO directory structure (train/valid/test folders with images/ and labels/
    subdirectories).

    Args:
        image_set: Dataset split to load. One of ``"train"``, ``"val"``, or
            ``"test"``.
        args: Argument namespace. The following attributes are consumed:
            ``dataset_dir``, ``square_resize_div_64``, ``aug_config``, ``segmentation_head``, ``multi_scale``,
            ``expanded_scales``, ``do_random_resize_via_padding``, ``patch_size``, ``num_windows``. ``aug_config`` is
            forwarded to the transform builder; when ``None`` the builder falls back to the default
            :data:`~rfdetr.datasets.aug_configs.AUG_CONFIG`.
        resolution: Target square resolution in pixels.

    Returns:
        A :class:`YoloDetection` dataset instance ready for use with a DataLoader.
    zprovided Roboflow path z does not existr   r   r   r   test)r   valrL  c              3  P   K   | ]  }|z  j                         s|z    y wrQ   )r   )rW   froots     r/   rY   z+build_roboflow_from_yolo.<locals>.<genexpr>I  s$     X1TAXDUDUDWdQhXs   &
&r   r   r   square_resize_div_64Fsegmentation_headmulti_scaleexpanded_scalesNdo_random_resize_via_padding
patch_sizenum_windows
aug_configuse_grouppose_keypointsr   keypoint_flip_pairsaugmentation_backendr0  zeYOLO keypoint training requires an Ultralytics pose data.yaml/data.yml with valid kpt_shape metadata.)rS  rT  skip_random_resizerV  rW  rX  gpu_postprocessrZ  )r   r   r_   rD  r&  r   r   )r   r  r   nextr  r   getattrr   r   r   r   r   r=  rV   r   r   )	image_setargs
resolutionPATHSr_   r   r   rQ  r&  rS  rT  rU  rV  rW  rX  r   r   rZ  resolved_augmentation_backendr]  r   r   rP  s                         @r/   build_roboflow_from_yolore  +  s   &   !D;;=I3D6II= .8+TG^h-FGw)4'>H+DE)4&=8+CDE X(@XZ^alZlmI!)//#"6q"9:J	"4)?GD"5u=M$u5Kd$5t<O#*41OQV#W |T2J$t4K|T2J&?G%d,ErJ<M,b	1	7RSW  %J'RVXnpuJv$w!3u<O	&y1 :)n)n9' /'C#C%'% /$7 (/$;%
R N)  :)n)n+' /'C#C%'% /$7 (/$;%
( N_ ":w7 	w	s   G   H4H  H)r*   r   rq   rg   )r5   rg   rq   rg   )rA   rg   rE   tuple[int, int]rq   rg   )rM   rj   rE   rf  rq   rg   )r\   rV   rq   r   )r_   rV   rq   r   )r*   r   r   rf   r   r   r   rf   r-   rf   r.   rf   r   rD   rq   z)tuple[int, np.ndarray, np.ndarray | None])r*   r   r   rf   r   r   r   rf   r-   rf   r.   rf   r   rf   r   rf   rq   z"tuple[int, np.ndarray, np.ndarray])r   rV   r   rV   r_   rV   r   rD   r   rD   r   YoloKeypointSchema | Nonerq   z'tuple[list[str], list[_LazyYoloSample]])r   rV   r   rV   r_   rV   rq   ry   )
r   rV   r   rV   r_   rV   r   r   rq   ry   rQ   )r{   r   r   r   r   rg  rq   r   )r  rV   rq   rD   )r`  rV   ra  r   rb  rf   rq   r=  )5
__future__r   r  dataclassesr   pathlibr   typingr   r   r1  r&   r+  rm   r	   PILr
   r   torchvision.datasetsr    rfdetr.datasets._keypoint_schemar   r   r   r   rfdetr.datasets.cocor   r   r   r  r  r  rU   r0   r6   r:   rH   rN   r]   ra   rd   ry   r   r   r   r   r   r   r  r!  r#  r=  re  rw   r1   r/   <module>rp     su   # 	 !  %  &   .   (4 ( !8, c =

&	X; $!M !M !MH" "@  X7X7X7 X7 	X7
 X7 X7 X7 /X7vL#L#L# L# 	L#
 L# L# L# L# (L#j $15mmm m
 m m /m -m`G,G GGG G (	G
 G* 26NNN /N 		Nbk0g! g!TZM Zzbr1   