o
    Ďi^                     @  s   d dl mZ d dlZd dlmZ d dlZd dlmZm	Z	 d dl
mZmZmZ d dlmZ eddG d	d
 d
ZeddG dd dZG dd dZG dd dZdS )    )annotationsN)	dataclass)CocoAnnotationCocoPrediction)get_bbox_from_coco_segmentation$get_bool_mask_from_coco_segmentation$get_coco_segmentation_from_bool_mask)ShapelyAnnotationT)frozenc                   @  s   e Zd ZU dZded< dZded< dd Zed	d
 Zedd Z	edd Z
edd Zedd Zedd Zedd Zd,d-ddZd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ ZdS ).BoundingBoxa  BoundingBox represents a rectangular region in 2D space, typically used for object detection annotations.

    Attributes:
        box (Tuple[float, float, float, float]): The bounding box coordinates in the format (minx, miny, maxx, maxy).
            - minx (float): Minimum x-coordinate (left).
            - miny (float): Minimum y-coordinate (top).
            - maxx (float): Maximum x-coordinate (right).
            - maxy (float): Maximum y-coordinate (bottom).
        shift_amount (Tuple[int, int], optional): The amount to shift the bounding box in the x and y directions.
            Defaults to (0, 0).

    !!! example "BoundingBox Usage Example"
        ```python
        bbox = BoundingBox((10.0, 20.0, 50.0, 80.0))
        area = bbox.area
        expanded_bbox = bbox.get_expanded_box(ratio=0.2)
        shifted_bbox = bbox.get_shifted_box()
        coco_format = bbox.to_coco_bbox()
        ```
    z/tuple[float, float, float, float] | list[float]box)r   r   ztuple[int, int]shift_amountc                 C  sD   t | jdkstdd | jD rtdt | jdkr tdd S )N   c                 s  s    | ]}|d k V  qdS )r   N ).0coordr   r   K/home/jeff/fluffinator/venv/lib/python3.10/site-packages/sahi/annotation.py	<genexpr>,   s    z,BoundingBox.__post_init__.<locals>.<genexpr>z;box must be 4 non-negative floats: [minx, miny, maxx, maxy]   z3shift_amount must be 2 integers: [shift_x, shift_y])lenr   any
ValueErrorr   selfr   r   r   __post_init__+   s
   "zBoundingBox.__post_init__c                 C  
   | j d S Nr   r   r   r   r   r   minx1      
zBoundingBox.minxc                 C  r   N   r   r   r   r   r   miny5   r   zBoundingBox.minyc                 C  r   )Nr   r   r   r   r   r   maxx9   r   zBoundingBox.maxxc                 C  r   )N   r   r   r   r   r   maxy=   r   zBoundingBox.maxyc                 C  r   r   r   r   r   r   r   shift_xA   r   zBoundingBox.shift_xc                 C  r   r    r&   r   r   r   r   shift_yE   r   zBoundingBox.shift_yc                 C  s   | j | j | j| j  S )N)r#   r   r%   r"   r   r   r   r   areaI   s   zBoundingBox.area皙?Nratiofloatmax_x
int | Nonemax_yc                 C  s   | j | j }| j| j }t|| }t|| }|r"t|| j | n| j | }td| j| }	|r9t|| j| n| j| }
td| j| }|	|||
g}t|S )a  Returns an expanded bounding box by increasing its size by a given ratio. The expansion is applied equally in
        all directions. Optionally, the expanded box can be clipped to maximum x and y boundaries.

        Args:
            ratio (float, optional): The proportion by which to expand the box size.
                Default is 0.1 (10%).
            max_x (int, optional): The maximum allowed x-coordinate for the expanded box.
                If None, no maximum is applied.
            max_y (int, optional): The maximum allowed y-coordinate for the expanded box.
                If None, no maximum is applied.

        Returns:
            BoundingBox: A new BoundingBox instance representing the expanded box.
        r   )r#   r   r%   r"   intminmaxr   )r   r+   r-   r/   why_marx_marr#   r   r%   r"   r   r   r   r   get_expanded_boxM   s   zBoundingBox.get_expanded_boxc                 C  s    | j | j| j| j  | j| j gS )zReturns [xmin, ymin, width, height]

        Returns:
            List[float]: A list containing the bounding box in the format [xmin, ymin, width, height].
        r   r"   r#   r%   r   r   r   r   to_xywhh   s    zBoundingBox.to_xywhc                 C     |   S )z
        Returns the bounding box in COCO format: [xmin, ymin, width, height]

        Returns:
            List[float]: A list containing the bounding box in COCO format.
        )r9   r   r   r   r   to_coco_bboxq      zBoundingBox.to_coco_bboxc                 C  s   | j | j| j| jgS )z
        Returns: [xmin, ymin, xmax, ymax]

        Returns:
            List[float]: A list containing the bounding box in the format [xmin, ymin, xmax, ymax].
        r8   r   r   r   r   to_xyxyz   s   zBoundingBox.to_xyxyc                 C  r:   )z
        Returns the bounding box in VOC format: [xmin, ymin, xmax, ymax]

        Returns:
            List[float]: A list containing the bounding box in VOC format.
        )r=   r   r   r   r   to_voc_bbox   r<   zBoundingBox.to_voc_bboxc                 C  s4   | j | j | j| j | j| j | j| j g}t|S )zReturns shifted BoundingBox.

        Returns:
            BoundingBox: A new BoundingBox instance representing the shifted box.
        )r   r'   r"   r(   r#   r%   r   )r   r   r   r   r   get_shifted_box   s   



zBoundingBox.get_shifted_boxc                 C  s8   d| j | j| j| jf d| j| j   d| j| j  dS )NzBoundingBox: <z, w: z, h: >r8   r   r   r   r   __repr__   s   

zBoundingBox.__repr__)r*   NN)r+   r,   r-   r.   r/   r.   )__name__
__module____qualname____doc____annotations__r   r   propertyr   r"   r#   r%   r'   r(   r)   r7   r9   r;   r=   r>   r?   rA   r   r   r   r   r      s4   
 






				r   c                   @  s2   e Zd ZU dZded< ded< dd Zdd	 Zd
S )CategoryzCategory of the annotation.

    Attributes:
        id (int): Unique identifier for the category.
        name (str): Name of the category.
    r0   idstrnamec                 C  s,   t | jts
tdt | jtstdd S )Nzid should be integerzname should be string)
isinstancerI   r0   	TypeErrorrK   rJ   r   r   r   r   r      s
   zCategory.__post_init__c                 C  s   d| j  d| j dS )NzCategory: <id: z, name: r@   rI   rK   r   r   r   r   rA      s   zCategory.__repr__N)rB   rC   rD   rE   rF   r   rA   r   r   r   r   rH      s   
 rH   c                   @  s   e Zd ZdZddgfd!d	d
Zedddgfd"ddZeddgfd#ddZed$ddZ	ed%ddZ
ed%ddZedd Zd&ddZd S )'Maska  Init Mask from coco segmentation representation.

    Args:
        segmentation : List[List]
            [
                [x1, y1, x2, y2, x3, y3, ...],
                [x1, y1, x2, y2, x3, y3, ...],
                ...
            ]
        full_shape: List[int]
            Size of the full image, should be in the form of [height, width]
        shift_amount: List[int]
            To shift the box and mask predictions from sliced image to full
            sized image, should be in the form of [shift_x, shift_y]
    r   segmentationlist[list[float]]
full_shape	list[int]r   listc                 C  sB   |d u rt d|d | _|d | _|d | _|d | _|| _d S )Nzfull_shape must be providedr   r!   )r   r'   r(   full_shape_heightfull_shape_widthrP   )r   rP   rR   r   r   r   r   __init__   s   




zMask.__init__g      ?mask
np.ndarraymask_thresholdr,   c                 C  s   ||k}| t |||dS )a6  
        Args:
            mask: np.ndarray of np.float elements
                Mask values between 0 and 1 (should have a shape of height*width)
            mask_threshold: float
                Value to threshold mask pixels between 0 and 1
            shift_amount: List
                To shift the box and mask predictions from sliced image
                to full sized image, should be in the form of [shift_x, shift_y]
            full_shape: List[int]
                Size of the full image after shifting, should be in the form of [height, width]
        rP   r   rR   r   )clsrX   rR   rZ   r   	bool_maskr   r   r   from_float_mask   s   zMask.from_float_maskr^   c                 C  s   | t |||dS )a  
        Args:
            bool_mask: np.ndarray with bool elements
                2D mask of object, should have a shape of height*width
            full_shape: List[int]
                Size of the full image, should be in the form of [height, width]
            shift_amount: List[int]
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        r[   r\   )r]   r^   rR   r   r   r   r   from_bool_mask   s
   zMask.from_bool_maskreturnc                 C  s   t | j| jd | jd dS )Nr!   r   )widthheight)r   rP   rR   r   r   r   r   r^   	  s   zMask.bool_maskc                 C  s   | j jd | j jd gS )z%Returns mask shape as [height, width]r   r!   )r^   shaper   r   r   r   rd     s   z
Mask.shapec                 C     | j | jgS )z9Returns full mask shape after shifting as [height, width])rU   rV   r   r   r   r   rR        zMask.full_shapec                 C  re   )z@Returns the shift amount of the mask slice as [shift_x, shift_y])r'   r(   r   r   r   r   r     rf   zMask.shift_amountc                   s   j d u s
jd u rtdg }jD ]1  fddtdt d dD } fddtdt dD }|dd t||D  qt|ddgj	d	S )
Nzfull_shape is Nonec                   "   g | ]}t j |  jqS r   )r1   r'   rV   r   isr   r   r   
<listcomp>$     " z)Mask.get_shifted_mask.<locals>.<listcomp>r   r!   r   c                   rg   r   )r1   r(   rU   rh   rj   r   r   rl   %  rm   c                 S  s   g | ]	}|D ]}|qqS r   r   )r   ri   jr   r   r   rl   &  s    r[   )
rU   rV   r   rP   ranger   appendziprO   rR   )r   shifted_segmentationxsysr   rj   r   get_shifted_mask  s   
$ zMask.get_shifted_maskN)rP   rQ   rR   rS   r   rT   )rX   rY   rR   rS   rZ   r,   r   rT   )r^   rY   rR   rS   r   rT   )ra   rY   )ra   rS   )ra   rO   )rB   rC   rD   rE   rW   classmethodr_   r`   rG   r^   rd   rR   r   ru   r   r   r   r   rO      s(    
rO   c                   @  s  e Zd ZdZddddddgdfd4ddZeddddgdfd5ddZeddddgfd6ddZeddddgdfd7ddZedddgfd8ddZ	eddddgfd9ddZ
eddgdfd:dd Zd;d#d$Zd<d&d'Zd=d(d)Zd*d+ Zd,d- Zed.d/ Zd0d1 Zd2d3 ZdS )>ObjectAnnotationz<All about an annotation such as Mask, Category, BoundingBox.Nr   bboxlist[int] | NonerP   np.ndarray | Nonecategory_idr.   category_name
str | Noner   rR   c                 C  s  t |ts	td|du r|du rtdd| _|dur3t|||d| _t|}|dur/|}ntdt|jdkrAt	|
 }t|d d}t|d d}	|rdt|d	 |d }
t|d
 |d }n|d	 }
|d
 }||	|
|g}t||| _|r||nt|}t||d| _d| _dS )a  
        Args:
            bbox: List
                [minx, miny, maxx, maxy]
            segmentation: List[List]
                [
                    [x1, y1, x2, y2, x3, y3, ...],
                    [x1, y1, x2, y2, x3, y3, ...],
                    ...
                ]
            category_id: int
                ID of the object category
            category_name: str
                Name of the object category
            shift_amount: List
                To shift the box and mask predictions from sliced image
                to full sized image, should be in the form of [shift_x, shift_y]
            full_shape: List
                Size of the full image after shifting, should be in
                the form of [height, width]
        zcategory_id must be an integerNz'you must provide a bbox or segmentationr[   zInvalid segmentation mask.numpyr   r!   r   r$   rN   )rL   r0   r   rX   rO   r   typerC   copydeepcopytolistr2   r1   r   rx   rJ   rH   categorymerged)r   rx   rP   r{   r|   r   rR   bbox_from_segmentationxminyminxmaxymaxr   r   r   rW   1  s@   

zObjectAnnotation.__init__c                 C  s   t |}| |||||dS )a  Creates ObjectAnnotation from bool_mask (2D np.ndarray)

        Args:
            bool_mask: np.ndarray with bool elements
                2D mask of object, should have a shape of height*width
            category_id: int
                ID of the object category
            category_name: str
                Name of the object category
            full_shape: List
                Size of the full image, should be in the form of [height, width]
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        r{   rP   r|   r   rR   r\   )r]   r^   r{   r|   r   rR   rP   r   r   r   r`   {  s   zObjectAnnotation.from_bool_maskrS   c                 C  s   | |||||dS )aJ  
        Creates ObjectAnnotation from coco segmentation:
        [
            [x1, y1, x2, y2, x3, y3, ...],
            [x1, y1, x2, y2, x3, y3, ...],
            ...
        ]

        Args:
            segmentation: List[List]
                [
                    [x1, y1, x2, y2, x3, y3, ...],
                    [x1, y1, x2, y2, x3, y3, ...],
                    ...
                ]
            category_id: int
                ID of the object category
            category_name: str
                Name of the object category
            full_shape: List
                Size of the full image, should be in the form of [height, width]
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        r   r   )r]   rP   rR   r{   r|   r   r   r   r   from_coco_segmentation  s   "z'ObjectAnnotation.from_coco_segmentationc           
      C  sN   |d }|d }|d |d  }|d |d  }	||||	g}| |||||dS )aV  Creates ObjectAnnotation from coco bbox [minx, miny, width, height]

        Args:
            bbox: List
                [minx, miny, width, height]
            category_id: int
                ID of the object category
            category_name: str
                Name of the object category
            full_shape: List
                Size of the full image, should be in the form of [height, width]
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        r   r!   r   r$   )r{   rx   r|   r   rR   r   )
r]   rx   r{   r|   r   rR   r   r   r   r   r   r   r   from_coco_bbox  s   zObjectAnnotation.from_coco_bboxannotation_dictdictc                 C  s@   |d r| j |d |d |||dS | j|d |d |||dS )a  Creates ObjectAnnotation object from category name and COCO formatted annotation dict (with fields "bbox",
        "segmentation", "category_id").

        Args:
            annotation_dict: dict
                COCO formatted annotation dict (with fields "bbox", "segmentation", "category_id")
            category_name: str
                Category name of the annotation
            full_shape: List
                Size of the full image, should be in the form of [height, width]
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        rP   r{   )rP   r{   r|   r   rR   rx   )rx   r{   r|   r   rR   )r   r   )r]   r   rR   r|   r   r   r   r   from_coco_annotation_dict  s   z*ObjectAnnotation.from_coco_annotation_dict
annotationr	   c                 C  s   | ||  |||dS )aF  Creates ObjectAnnotation from shapely_utils.ShapelyAnnotation.

        Args:
            annotation: shapely_utils.ShapelyAnnotation
            category_id: int
                ID of the object category
            category_name: str
                Name of the object category
            full_shape: List
                Size of the full image, should be in the form of [height, width]
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
        r   )to_coco_segmentation)r]   r   rR   r{   r|   r   r   r   r   from_shapely_annotation  s   z(ObjectAnnotation.from_shapely_annotationc                 C  s   | |j j|jj|j j||dS )a  Creates ObjectAnnotation from imantics.annotation.Annotation.

        Args:
            annotation: imantics.annotation.Annotation
            shift_amount: List
                To shift the box and mask predictions from sliced image to full
                sized image, should be in the form of [shift_x, shift_y]
            full_shape: List
                Size of the full image, should be in the form of [height, width]
        )r{   r^   r|   r   rR   )r   rI   rX   arrayrK   )r]   r   r   rR   r   r   r   from_imantics_annotation1  s   z)ObjectAnnotation.from_imantics_annotationra   r   c                 C  sH   | j rtj| j j| jj| jjd}|S tj| j	 | jj| jjd}|S )zJReturns sahi.utils.coco.CocoAnnotation representation of ObjectAnnotation.)rP   r{   r|   )rx   r{   r|   )
rX   r   r   rP   r   rI   rK   r   rx   r9   )r   coco_annotationr   r   r   to_coco_annotationJ  s   z#ObjectAnnotation.to_coco_annotationr   c                 C  sL   | j rtj| j j| jj| jjdd}|S tj| j	 | jj| jjdd}|S )zJReturns sahi.utils.coco.CocoPrediction representation of ObjectAnnotation.r!   )rP   r{   r|   score)rx   r{   r|   r   )
rX   r   r   rP   r   rI   rK   r   rx   r9   )r   coco_predictionr   r   r   to_coco_predictionZ  s   z#ObjectAnnotation.to_coco_predictionc                 C  s0   | j rtj| j jd}|S tj| j d}|S )zPReturns sahi.utils.shapely.ShapelyAnnotation representation of ObjectAnnotation.)rP   )rx   )rX   r	   r   rP   r   rx   r9   )r   shapely_annotationr   r   r   to_shapely_annotationl  s   z&ObjectAnnotation.to_shapely_annotationc                 C  s   zddl }W n ty   tdw |j| jj| jjd}| jdur5|j| jj	}|j
jj||d}|S |j| j }|j
jj||d}|S )zJReturns imantics.annotation.Annotation representation of ObjectAnnotation.r   NzWPlease run "pip install -U imantics" to install imantics first for imantics conversion.rN   )rX   r   )rx   r   )imanticsImportErrorrH   r   rI   rK   rX   rO   creater^   r   
Annotation	from_maskBBoxrx   r=   	from_bbox)r   r   imantics_categoryimantics_maskimantics_annotationimantics_bboxr   r   r   to_imantics_annotationx  s"   
z'ObjectAnnotation.to_imantics_annotationc                 C  s
   t | S )zH
        Returns: deepcopy of current ObjectAnnotation instance
        )r   r   r   r   r   r   r     s   
zObjectAnnotation.deepcopyc                 C  s
   t d dS )N)r^   )rO   )r]   r   r   r   get_empty_mask  r   zObjectAnnotation.get_empty_maskc                 C  sh   | j r| j  }t| j  | jj|j| jj	ddg|j
dS t| j  | jjd | jj	ddgd dS )Nr   )rx   r{   rP   r|   r   rR   )rx   r{   r^   r|   r   rR   )rX   ru   rw   rx   r?   r=   r   rI   rP   rK   rR   )r   shifted_maskr   r   r   get_shifted_object_annotation  s$   
	z.ObjectAnnotation.get_shifted_object_annotationc                 C  s   d| j  d| j d| j dS )NzObjectAnnotation<
    bbox: z,
    mask: z,
    category: r@   )rx   rX   r   r   r   r   r   rA     s   zObjectAnnotation.__repr__)rx   ry   rP   rz   r{   r.   r|   r}   r   ry   rR   ry   )r{   r.   r|   r}   r   ry   rR   ry   )rR   rS   r{   r.   r|   r}   r   ry   )
rx   rS   r{   r.   r|   r}   r   ry   rR   ry   )r   r   rR   rS   r|   r}   r   ry   )
r   r	   rR   rS   r{   r.   r|   r}   r   ry   )r   ry   rR   ry   )ra   r   )ra   r   )ra   r	   )rB   rC   rD   rE   rW   rv   r`   r   r   r   r   r   r   r   r   r   r   r   r   rA   r   r   r   r   rw   .  s`    J )$&



rw   )
__future__r   r   dataclassesr   r~   npsahi.utils.cocor   r   sahi.utils.cvr   r   r   sahi.utils.shapelyr	   r   rH   rO   rw   r   r   r   r   <module>   s     w