
    ^jL                    |   d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	 ddl
mZ ddlmZ  e       Zg dZ ed	d	
       G d d             Z ed	d	
       G d d             Zeez  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d,dZd-dZd.dZd/dZ dd	 	 	 	 	 d0dZ!dd	 	 	 	 	 d1d Z"d2d!Z#y)3z4Private helpers for COCO keypoint schema extraction.    )annotationsN)	dataclassfield)Path)Any)
get_logger)CocoKeypointSchemaKeypointSchemaYoloKeypointSchemaactive_keypoint_countsinfer_coco_keypoint_schemainfer_yolo_keypoint_schemaT)frozenslotsc                  L    e Zd ZU dZded<   ded<   ded<    ee      Zded	<   y
)r	   ax  Keypoint schema inferred from COCO category metadata.

    Args:
        class_names: Category names sorted by category id.
        num_keypoints_per_class: Number of keypoints for each sorted category.
        keypoint_oks_sigmas: Default OKS sigmas matching the largest keypoint class.
        keypoint_flip_pairs: Flat horizontal-flip swap pairs inferred from keypoint names.

    Returns:
        Immutable schema value used to configure keypoint training.

    Raises:
        This value object does not raise.

    Example:
        >>> CocoKeypointSchema(["person"], [17], [0.1] * 17).num_keypoints_per_class
        [17]
    	list[str]class_names	list[int]num_keypoints_per_classlist[float]keypoint_oks_sigmasdefault_factorykeypoint_flip_pairsN__name__
__module____qualname____doc____annotations__r   listr        k/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/datasets/_keypoint_schema.pyr	   r	      s+    & &&$$%*4%@@r#   r	   c                  j    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<    ee      Zded<   y)r   a  Keypoint schema inferred from an Ultralytics YOLO pose YAML file.

    Args:
        class_names: Class names ordered by YOLO class id.
        num_keypoints_per_class: Number of keypoints for each YOLO class slot.
        keypoint_oks_sigmas: Default OKS sigmas matching the global keypoint count.
        keypoint_names: Keypoint names ordered by keypoint index.
        flip_idx: Optional Ultralytics horizontal-flip index mapping.
        keypoint_dim: Number of keypoint dimensions in label files, either 2 or 3.

    Returns:
        Immutable schema value used to configure YOLO pose training.

    Raises:
        This value object does not raise.

    Example:
        >>> YoloKeypointSchema(["person"], [1], [0.1], ["nose"], [], 3).keypoint_dim
        3
    r   r   r   r   r   r   keypoint_namesflip_idxintkeypoint_dimr   r   Nr   r"   r#   r$   r   r   9   s=    * &&$$%*4%@@r#   r   c                    ddl }| j                  d      5 }|j                  |      }ddd       t        t              s.t        dt        |       dt        |      j                   d      |S # 1 sw Y   IxY w)a  Load a YAML file and require a mapping root.

    Args:
        yaml_path: Path to a YAML data file.

    Returns:
        Parsed YAML mapping.

    Raises:
        ValueError: If the YAML root is not a mapping.
        OSError: If the file cannot be read.

    Example:
        >>> import tempfile
        >>> path = Path(tempfile.mkdtemp()) / "data.yaml"
        >>> _ = path.write_text("names: [person]\nkpt_shape: [1, 3]\n", encoding="utf-8")
        >>> sorted(_load_yaml_mapping(path))
        ['kpt_shape', 'names']
    r   Nutf-8encodingzExpected mapping in data file , got .)	yamlopen	safe_load
isinstancedict
ValueErrorstrtyper   )	yaml_pathr0   filedatas       r$   _load_yaml_mappingr;   ]   sx    ( 		) $T~~d#$dD!9#i.9K6RVW[R\ReReQffghiiK	$ $s   A11A:c                   | j                  d      }t        |t              rg }g }|j                         D ]I  }t	        |      }|j                         r|j                  t        |             9|j                  |       K t        t        |            }|r"|t        t        t        |                  k7  s|rt        dt	        |      d      |D cg c]  }t	        ||          c}S t        |t              r|D 	cg c]  }	t	        |	       c}	S t        dt	        |      dt        |      j                   d      c c}w c c}	w )z:Extract contiguous YOLO class names from parsed YAML data.namesz)Unsupported 'names' mapping in data file z,: expected integer keys 0..N-1 with no gaps.z)Expected 'names' to be a list or dict in r.   r/   )getr3   r4   keysr6   isdigitappendr(   sortedsetr!   rangelenr5   r7   r   )
r:   	data_filer=   numeric_keysnon_numeric_keyskeykey_strunique_sorted_keysidxnames
             r$   #_extract_yolo_class_names_from_datarN   z   s2   HHWE%"$&(::< 	-C#hG ##CL1 '',	- $C$56!%74cJ\F]@^;_%_cs;y>$$PR  ,>>CE#J>>%&+,dD	,,
@Y@RRXY]^cYdYmYmXnnop
qq ?,s   EE
c                j   t        | t        t        f      rt        |       dk7  rt	        dt        |      d      	 t        | d         }t        | d         }|dk  s|d	vrt	        dt        |      d| d
      ||fS # t        t        f$ r!}t	        dt        |      d| d      |d}~ww xY w)z7Validate and normalize a YOLO pose ``kpt_shape`` entry.   YOLO pose data file z2 must define kpt_shape as [num_keypoints, 2_or_3].r      z has invalid kpt_shape=z; expected integer values.N)rP      z,; expected [positive_num_keypoints, 2_or_3].)r3   r!   tuplerE   r5   r6   r(   	TypeError)raw_kpt_shaperF   num_keypointsr)   excs        r$   _validate_yolo_kpt_shaperY      s    mdE]3s=7IQ7N/I/AAstuuM!,-=+,
 \7"3y>"44KMK\ ]9 9
 	
 ,&& z" "3y>"44KMK\\vw
	s   B B2B--B2c                   | j                  d      }d}t        |t              r$|r"|j                  d|j                  d            }nt        |t              r|}|t	        |      D cg c]  }d| 	 c}S t        |t              rt        |      |k7  rt        d| d|d      |D cg c]  }t        |       c}S c c}w c c}w )	z>Extract YOLO keypoint names or synthesize stable placeholders.	kpt_namesNr   0	keypoint_z?YOLO pose kpt_names length must match kpt_shape keypoint count r.   r/   )r>   r3   r4   r!   rD   rE   r5   r6   )r:   rW   raw_kpt_namesr&   rL   rM   s         r$   _extract_yolo_keypoint_namesr_      s    HH[)MN-&=&**1m.?.?.DE	M4	(&-2=-ABc)C5!BBnd+s>/Bm/SMm_\bcqbttuv
 	
 #11$CI11 C
 2s   )C,Cc                p   | j                  d      }|g S t        |t              rt        |      |k7  rt	        d| d      	 |D cg c]  }t        |       }}t        |      t        t        |            k7  rt	        d|dz
   d      |S c c}w # t        t        f$ r}t	        d      |d}~ww xY w)	z9Extract and validate optional YOLO ``flip_idx`` metadata.r'   Nz YOLO pose flip_idx must contain z integer indexes.z0YOLO pose flip_idx must contain integer indexes.z/YOLO pose flip_idx must be a permutation of 0..rR   r/   )	r>   r3   r!   rE   r5   r(   rU   rB   rD   )r:   rW   raw_flip_idxrL   r'   rX   s         r$   _extract_yolo_flip_idxrb      s    88J'L	lD)S->--O;M?J[\]]V(45CH55 h4m 455J=[\K\J]]^_``O 6z" VKLRUUVs*   B BB B B5$B00B5c                    g }t               }t        |       D ]b  \  }}||v s	||v s||k(  r|j                  |       %|t        |       k  s4| |   |k(  s=|j	                  ||g       |j                  ||h       d |S )aK  Convert a YOLO flip_idx permutation to flat swap pairs.

    Args:
        flip_idx: Full permutation where ``flip_idx[i]`` is the horizontal mirror of joint ``i``.

    Returns:
        Flat list ``[a0, b0, a1, b1, ...]`` of left/right joint index pairs.

    Example:
        >>> _flip_idx_to_pairs([0, 2, 1])
        [1, 2]
    )rC   	enumerateaddrE   extendupdate)r'   pairsseenrL   
mirror_idxs        r$   _flip_idx_to_pairsrk      s     EUD$X. +Z$;*,z0AHHSMH%(:*>#*ELL#z*+KKj)*+ Lr#   c                    t        j                  ddt        |       j                         j	                               j                  d      S )z0Normalize a keypoint name for symmetry matching.z
[^a-z0-9]+_)resubr6   striplower)rM   s    r$   _normalize_keypoint_namerr      s4    66-c$ioo&7&=&=&?@FFsKKr#   c                    | j                  d      }|j                  d      }|j                  d      }||z   dk7  ry|D cg c]  }|dk(  rdn|dk(  rdn| }}dj                  |      S c c}w )zAReturn the left/right counterpart for a normalized keypoint name.rm   leftrightrR   N)splitcountjoin)normalized_nametokens
left_countright_counttokenmirror_tokenss         r$   _mirror_keypoint_namer      s|    ""3'Ff%J,,w'KK1$hno_dWu?OVUZZoMo88M"" ps   A+c                (   | D cg c]  }t        |       }}t        t        |            t        |      k7  rg S t        |      D ci c]  \  }}||
 }}}g }t               }t        |      D ]m  \  }}||v rt	        |      }||j                  |      }	|		|	|k(  s|	|v r6t	        ||	         |k7  rH|j                  ||	g       |j                  ||	h       o |s%| r#t        j                  dt        |       | dd        |S c c}w c c}}w )z@Infer horizontal-flip swap pairs from left/right keypoint names.NzInferred 0 flip pairs from %d keypoint names %s. Automatic horizontal-flip slot swapping is disabled for this category. Provide keypoint_flip_pairs explicitly if your keypoints have left/right symmetry.   )
rr   rE   rC   rd   r   r>   rf   rg   loggerinfo)
r&   rM   normalized_namesrL   index_by_namerh   ri   ry   mirror_namerj   s
             r$   %_infer_keypoint_flip_pairs_from_namesr      s=   CQR406RR
3 !S)9%::	09:J0KL93T3YLMLEUD )*: ; '_$;+O<"&&{3
s!2jD6H !1*!=>/Qc:&'S*%&' ^a 2A	
 L9 S Ms   D		Dc                    | sg S t        |       dk(  rt        | d         S | d   t        fd| dd D              rt              S g S )zDReturn one global flip-pair list when all keypoint categories agree.rR   r   c              3  (   K   | ]	  }|k(    y w)Nr"   ).0rh   firsts     r$   	<genexpr>z6_merge_category_keypoint_flip_pairs.<locals>.<genexpr>  s     
:e5E>
:s   N)rE   r!   all)category_pairsr   s    @r$   #_merge_category_keypoint_flip_pairsr     sV    	
>aN1%&&1E

:~ab'9
::E{Ir#   c                    | j                  d      5 }t        j                  |      }ddd       t        t              s"t        dt        |      j                   d      |S # 1 sw Y   =xY w)aJ  Load a COCO annotation JSON file.

    Args:
        annotation_path: Path to a COCO annotation JSON file.

    Returns:
        Parsed COCO annotation mapping.

    Raises:
        ValueError: If the JSON root is not an object.
        OSError: If the file cannot be read.

    Example:
        >>> import tempfile
        >>> path = Path(tempfile.mkdtemp()) / "annotations.json"
        >>> _ = path.write_text('{"images": [], "annotations": [], "categories": []}', encoding="utf-8")
        >>> sorted(_load_coco_annotation(path))
        ['annotations', 'categories', 'images']
    r+   r,   Nz3Expected COCO annotation root to be an object, got r/   )r1   jsonloadr3   r4   r5   r7   r   )annotation_pathr9   r:   s      r$   _load_coco_annotationr      sj    ( 
		w		/ 4yydD!NtTXzObObNccdeffK	 s   A%%A.c                :   t        | t              r| st        d      g }| D ]j  }t        |t              s"t        dt	        |      j
                   d      d|vrt        d      d|vrt        d|d   d      |j                  |       l t        |d	 
      S )a  Validate and sort COCO categories by category id.

    Args:
        categories: Raw ``categories`` value from a COCO annotation file.

    Returns:
        Category dictionaries sorted by ``id``.

    Raises:
        ValueError: If categories are missing or malformed.

    Example:
        >>> _validate_categories([{"id": 2, "name": "b"}, {"id": 1, "name": "a"}])
        [{'id': 1, 'name': 'a'}, {'id': 2, 'name': 'b'}]
    zCExpected COCO annotations to contain a non-empty 'categories' list.z1Expected each COCO category to be an object, got r/   idz5Expected each COCO category to contain an 'id' field.rM   zExpected COCO category_id z to contain a 'name' field.c                    t        | d         S )Nr   )r(   )items    r$   <lambda>z&_validate_categories.<locals>.<lambda>W  s    c$t*o r#   )rI   )r3   r!   r5   r4   r7   r   rA   rB   )
categories	validatedcategorys      r$   _validate_categoriesr   ;  s      j$'z^__&(I #(D)PQUV^Q_QhQhPiijkllxTUU!9(4.9KKfghh"# )!=>>r#   c                    t        | t              st        d      | D ]4  }t        |t              rt        dt	        |      j
                   d       | S )ag  Validate COCO annotations container type.

    Args:
        annotations: Raw ``annotations`` value from a COCO annotation file.

    Returns:
        COCO annotation dictionaries.

    Raises:
        ValueError: If annotations are not a list of objects.

    Example:
        >>> _validate_annotations([{"category_id": 1}])
        [{'category_id': 1}]
    z;Expected COCO annotations to contain an 'annotations' list.z3Expected each COCO annotation to be an object, got r/   )r3   r!   r5   r4   r7   r   )r   
annotations     r$   _validate_annotationsr   Z  sa      k4(VWW! q
*d+RSWXbScSlSlRmmnoppq r#   c           	     2   d}| D ]  }t        |j                  dd            |k7  r"|j                  d      }||g k(  r;t        |t              sLt	        |      dz  dk7  rt        d|dt	        |       d      t        |t	        |      dz        } |S )	a  Infer keypoint count for one category from annotation vectors.

    Args:
        annotations: COCO annotation dictionaries.
        category_id: Category id whose annotations should be inspected.

    Returns:
        Maximum keypoint vector length found for the category.

    Raises:
        ValueError: If keypoint annotation length is not divisible by three.

    Example:
        >>> anns = [{"category_id": 1, "keypoints": [1, 2, 2, 3, 4, 2]}]
        >>> _keypoint_count_from_annotations(anns, 1)
        2
    r   category_id	keypointsrS   z COCO annotation for category_id z has zL keypoint values; expected a flat [x, y, v] list with length divisible by 3.)r(   r>   r3   r!   rE   r5   max)r   r   keypoint_countr   raw_keypointss        r$    _keypoint_count_from_annotationsr   r  s    $ N! F
z~~mR01[@"{3 MR$7-.}!Q&2;/s=GYFZ [M M  ^S-?1-DEF r#   g?)keypoint_oks_sigmac                  t        |       }t        |      }t        |d         }t        |j	                  dg             }g }g }g }|D ]  }	t        |	d         }
|j                  t        |	d                |	j	                  d      }t        |t              r7|r5|j                  t        |             |j                  t        |             t        ||
      }|j                  |       |dkD  s|j                  g         t        d |D              st        d| d	      t        |d
      }t!        |||g|z  t#        |            S )aZ  Infer a keypoint schema from a COCO annotation JSON file.

    Args:
        annotation_path: Path to a COCO annotation JSON file.
        keypoint_oks_sigma: Default OKS sigma to repeat for the largest keypoint class.

    Returns:
        Category-aligned class names, ``num_keypoints_per_class``, and OKS sigmas.

    Raises:
        FileNotFoundError: If the annotation file does not exist.
        ValueError: If the annotation file has no categories, no keypoint metadata,
            malformed COCO fields, or malformed keypoint vectors.
        KeyError: If required COCO keys are missing.
    r   r   r   rM   r   r   c              3  &   K   | ]	  }|d kD    yw)r   Nr"   )r   rw   s     r$   r   z-infer_coco_keypoint_schema.<locals>.<genexpr>  s     >Uuqy>s   zCOCO annotation file 'za' has no keypoint metadata. Expected category 'keypoints' entries or annotation keypoint vectors.)default)r   r   r   r   )r   r   r   r   r>   r(   rA   r6   r3   r!   rE   r   r   anyr5   r   r	   r   )r   r   pathr:   r   r   r   r   category_flip_pairsr   r   category_keypointsinferred_countmax_keypointss                 r$   r   r     s_   (  D &D%d<&89J'(CDKK)++- /(4.)3x/01%\\+6($/4F#**3/A+BC&&'LM_'`a=k;WN#**>:!#**2./ >&=>>$TF +T T
 	

 /;M 7/0=@?@ST	 r#   c               *   t        |       }t        |      }t        ||      }d|vrt        dt	        |      d      t        |d   |      \  }}t        ||      }t        ||      }t        ||gt        |      z  |g|z  |||t        |            S )a  Infer a keypoint schema from an Ultralytics YOLO pose YAML file.

    Args:
        data_file: Path to a YOLO ``data.yaml`` or ``data.yml`` file.
        keypoint_oks_sigma: Default OKS sigma to repeat for the global keypoint count.

    Returns:
        Class-aligned keypoint counts plus optional YOLO keypoint names and flip indexes.

    Raises:
        FileNotFoundError: If the YAML file does not exist.
        ValueError: If required YOLO pose fields are missing or malformed.

    Example:
        >>> import tempfile
        >>> path = Path(tempfile.mkdtemp()) / "data.yaml"
        >>> _ = path.write_text("names: [person]\nkpt_shape: [1, 3]\n", encoding="utf-8")
        >>> infer_yolo_keypoint_schema(path).num_keypoints_per_class
        [1]
    	kpt_shaperQ   z( is missing required kpt_shape metadata.)r   r   r   r&   r'   r)   r   )r   r;   rN   r5   r6   rY   r_   rb   r   rE   rk   )	rF   r   r   r:   r   rW   r)   r&   r'   s	            r$   r   r     s    2 	?Dd#D5dDAK$/D	}<deff":4;Ld"SM<1$FN%dM:H!.#k2B B/0=@%!.x8 r#   c                8    | D cg c]
  }|dkD  s	| c}S c c}w )a<  Return non-zero keypoint counts from a model schema.

    Args:
        num_keypoints_per_class: Model keypoint schema.

    Returns:
        Positive keypoint counts in schema order.

    Raises:
        This helper does not raise.

    Example:
        >>> active_keypoint_counts([0, 17, 25])
        [17, 25]
    r   r"   )r   rw   s     r$   r   r     s       7De%!)EDDDs   
)r8   r   returndict[str, Any])r:   r   rF   r   r   r   )rV   r   rF   r   r   ztuple[int, int])r:   r   rW   r(   r   r   )r:   r   rW   r(   r   r   )r'   r   r   r   )rM   r   r   r6   )ry   r6   r   z
str | None)r&   z	list[Any]r   r   )r   zlist[list[int]]r   r   )r   r   r   r   )r   r   r   list[dict[str, Any]])r   r   r   r   )r   r   r   r(   r   r(   )r   
str | Pathr   floatr   r	   )rF   r   r   r   r   r   )r   r   r   r   )$r   
__future__r   r   rn   dataclassesr   r   pathlibr   typingr   rfdetr.utilities.loggerr   r   __all__r	   r   r
   r;   rN   rY   r_   rb   rk   rr   r   r   r   r   r   r   r   r   r   r   r"   r#   r$   <module>r      s   ; "  	 (   .	 $d#A A $A4 $d#A A $A@ $&88:r2'&2$ 2L
	#B
6?>0!N !$55 5 	5v !$)) ) 	)XEr#   