
    ^jʿ                       U d Z ddlmZ ddl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mZ ddZ	 	 	 	 	 	 	 	 dd	Z	 	 	 	 	 	 	 	 dd
Z	 	 	 	 	 	 ddZ	 	 	 	 	 	 	 	 	 	 	 	 ddZdZded<   dZded<   	 	 	 	 	 	 	 	 	 	 	 	 ddZ G d d      Zy)u  Crop-RLE compact mask storage for memory-efficient instance segmentation.

Dense ``(N, H, W)`` boolean masks use O(N·H·W) memory, which becomes
prohibitive for aerial imagery (e.g. 1000 objects x 4K image ~ 8.3 GB).
:class:`CompactMask` stores each mask as a run-length encoding of its
bounding-box crop, reducing typical usage to tens of MB.

The bounding boxes (``xyxy``) already present in ``Detections`` serve as the
crop boundaries, so no extra metadata is required from the caller.
    )annotationsN)Iterator)Any)_mask_to_rle_counts_rle_counts_to_maskintc                J    t        t        j                  | ddd               S )a  Return the number of ``True`` pixels in a run-length encoded mask.

    Args:
        rle: int32 array of run lengths as produced by :func:`_mask_to_rle_counts`.

    Returns:
        Total number of ``True`` pixels.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import _rle_area
        >>> rle = np.array([1, 2, 1, 1, 1], dtype=np.int32)
        >>> _rle_area(rle)
        3

        ```
       N   )r   npsum)rles    m/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/supervision/detection/compact_mask.py	_rle_arear      s"    & rvvc!$Q$i !!    c                8   t        |      D cg c]  }g  }}d}d}t        |       D ]  \  }}|dz  dk(  }	t        |      }
|
dkD  r||z
  }t        |
|      }t	        ||         dk(  r|	rV||   j                  d       nA|	t	        ||         dz
  dz  dk(  k(  r'||   dxx   |z  cc<   |
|z  }
||z  }||k\  rd}|dz  }||   j                  |       |
|z  }
||z  }||k\  rd}|dz  }|
dkD  r||k\  s n t        |      D ]  }||   r	|g||<    |S c c}w )a  Split a flat F-order RLE into per-column run lists.

    With F-order (column-major) RLE the flat pixel sequence visits all rows
    of column 0, then all rows of column 1, etc.  Each column therefore
    contains ``crop_h`` contiguous pixels.

    Runs that cross column boundaries are split at the boundary.  Each
    returned list starts with a ``False``-run count (possibly 0), matching
    the convention of :func:`_mask_to_rle_counts`.

    Args:
        rle: int32 run-length array as produced by
            :func:`~supervision.detection.utils.converters._mask_to_rle_counts`.
        crop_h: Number of rows (pixels per column).
        crop_w: Number of columns.

    Returns:
        List of ``crop_w`` run lists, one per column.  Each list sums to
        ``crop_h``.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import _rle_split_cols
        >>> from supervision.detection.utils.converters import _mask_to_rle_counts
        >>> mask = np.array([[True, False], [True, True]], dtype=bool)
        >>> rle = _mask_to_rle_counts(mask)
        >>> rle.tolist()
        [0, 2, 1, 1]
        >>> _rle_split_cols(rle, 2, 2)
        [[0, 2], [1, 1]]

        ```
    r   r   r
   )range	enumerater   minlenappend)r   crop_hcrop_w_per_colcolrowrun_idxrun_lenis_true	remainingspace_in_coltakecs                 r   _rle_split_colsr&   1   s{   N -2&M:q:G:
C
C%cN A+"L	!m!C<Ly,/D73< A%CL''* c'#,/!3q8A=>R D( T!	t&=C1HCCL%I4KCf}q+ !m, &=5: 6] "qz GAJ" NK  ;s   	Dc                   t        |      }|dk(  rdgS t        j                  |t        j                        }d}t	        |       D ]  \  }}|dz  dk(  ||||z    ||z  } ||k  rd||d ||   }|j                  t        j                        }	t        j                  t        j                  |	            }
t        j                  t        |
      dz   t        j                        }t        j                  t        |
      dz   t        j                        }d|d<   |
dz   |dd |
dz   |dd ||d<   ||z
  j                         }t        |d         r|j                  dd       |S )uZ  Scale one column's run list to a new height using a precomputed row map.

    Each output row is mapped to a source row via ``row_map``, which
    implements nearest-neighbour resampling in the vertical direction.

    Args:
        col_runs: Per-column run list starting with a ``False``-run count.
        src_h: Height of the source column (sum of ``col_runs``).
        row_map: int32 array of length ``new_crop_h``; ``row_map[r']`` is the
            source row index for output row ``r'``.  Use
            ``(np.arange(new_crop_h) * src_h // new_crop_h)`` for
            ``cv2.INTER_NEAREST``-compatible mapping.

    Returns:
        Scaled run list of total length ``len(row_map)``, always starting
        with a ``False``-run count.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import _rle_scale_col
        >>> col_runs = [0, 2, 2]   # F=0, T=2, F=2  → [T, T, F, F]
        >>> row_map = np.array([0, 1, 2, 3, 0, 1, 2, 3], dtype=np.int32)
        >>> _rle_scale_col(col_runs, 4, row_map)
        [0, 2, 2, 2, 2]

        ```
    r   dtyper   r
   FNr   )r   r   emptybool_r   viewuint8flatnonzerodiffint64tolistboolinsert)col_runssrc_hrow_map
new_crop_h
src_valuesposrirl
out_values	out_uint8
boundaries
run_startsrun_endsresult_runss                 r   _rle_scale_colrB      s[   B WJQs
 )+bhh(GJ
CH% B%'!Vq[
3r"r	 U{ 
34 G$J )I	 23J(*Z11DBHH(UJ&(hhs:/B"((&SHJqM!^JqrNNHSbMHRL&3;;=KJqM1a r   c                   g }| D ]  }|s|j                  |       t        |      dz
  dz  dk(  }|s%|dxx   |d   z  cc<   |j                  |dd        R|d   dk(  r3t        |      dkD  r%|dxx   |d   z  cc<   |j                  |dd        |j                  |        t        j                  |r|n|gt        j                        S )a  Concatenate per-column run lists into a flat RLE, merging junctions.

    Each column run list starts with a ``False``-run count. Two junction types
    can be merged across column boundaries:

    * ``False``/``False``: the trailing False run merges with the leading False
      run of the next column (leading count may be zero).
    * ``True``/``True``: when the accumulated output ends on a True run and the
      next column's leading False count is zero (column starts with True), the
      two True runs are merged to avoid inserting a zero-length False run that
      would inflate ``len(rle)`` and skew the density metric in
      :func:`_resize_crop`.

    Args:
        scaled_cols: List of per-column run lists, each starting with a
            ``False``-run count.
        new_total: Total pixel count of the output (fallback for empty input).

    Returns:
        Flat int32 RLE array starting with a ``False``-run count.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import _rle_join_cols
        >>> cols = [[1, 2], [1, 2]]  # each col: F=1, T=2
        >>> _rle_join_cols(cols, 6).tolist()
        [1, 2, 1, 2]

        ```
    r
   r   r   r   Nr(   )extendr   r   arrayint32)scaled_cols	new_totaloutput_runsr4   last_is_trues        r   _rle_join_colsrK      s    F  K -x(,q0A5:LB8A;.""8AB<0!!c(ma&7 B8A;.""8AB<0""8,-" 88;KYKrxxPPr   c                    ||z  }||z  dk(  s|dk(  r&t        j                  dgt         j                        S t        |       dk(  s't	        t        j
                  | ddd               dk(  r&t        j                  |gt         j                        S t        |       dk(  r/| d   dk(  r't        j                  d|gt         j                        S t        | ||      }t        j                  |      |z  |z  j                  t         j                        }t        j                  |      |z  |z  j                  t         j                        }i }	g }
|D ]-  }||	vrt        ||   ||      |	|<   |
j                  |	|          / t        |
|      S )a   Resize an F-order RLE-encoded crop via nearest-neighbour resampling.

    Manipulates run lengths directly without decoding to a full 2D boolean
    array.  Delegates to :func:`_rle_split_cols`, :func:`_rle_scale_col`,
    and :func:`_rle_join_cols`.

    The nearest-neighbour mapping ``src = floor(dst * src_size / dst_size)``
    is bit-exact with ``cv2.INTER_NEAREST``.

    Args:
        rle: int32 array of F-order run lengths as produced by
            :func:`~supervision.detection.utils.converters._mask_to_rle_counts`.
            Starts with a ``False``-run count (may be 0).
        crop_h: Height of the original crop.
        crop_w: Width of the original crop.
        new_crop_h: Height of the resized crop.
        new_crop_w: Width of the resized crop.

    Returns:
        int32 array of F-order run lengths for the resized crop, starting
        with the ``False``-run count.

    Examples:
        Upscale a 3x3 mask with a diagonal True stripe to 6x6:

        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import _rle_resize
        >>> from supervision.detection.utils.converters import (
        ...     _mask_to_rle_counts, _rle_counts_to_mask,
        ... )
        >>> mask = np.array([
        ...     [True,  False, False],
        ...     [False, True,  False],
        ...     [False, False, True ],
        ... ], dtype=bool)
        >>> rle = _mask_to_rle_counts(mask)
        >>> resized_rle = _rle_resize(rle, 3, 3, 6, 6)
        >>> result = _rle_counts_to_mask(resized_rle, 6, 6)
        >>> result.astype(int)
        array([[1, 1, 0, 0, 0, 0],
               [1, 1, 0, 0, 0, 0],
               [0, 0, 1, 1, 0, 0],
               [0, 0, 1, 1, 0, 0],
               [0, 0, 0, 0, 1, 1],
               [0, 0, 0, 0, 1, 1]])

        ```
    r   r(   r
   Nr   )r   rE   rF   r   r   r   r&   arangeastyperB   r   rK   )r   r   r   r7   
new_crop_wrH   r   col_mapr6   	col_cacherG   src_cs               r   _rle_resizerS      sc   p Z'I!yA~xx288,,
3x1}BFF3qt!t9-.!3xx28844
3x1}Q1xxIbhh77c662G yy$v-;CCBHHMG yy$v-;CCBHHMG ')IK -	!-genfgNIe9U+,-
 +y11r   g      ?float_L3_DENSITY_THRESHOLD   _PARALLEL_THRESHOLDc                   ddl }t        |       dk(  r)t        j                  ||z  gt        j                        S t        |       t        d||z        z  t        k  rt        | ||||      S t        | ||      }|j                  |j                  t        j                        ||f|j                        j                  t              }t!        |      S )u  Resize one RLE crop to ``(new_h, new_w)``, choosing the fastest path.

    Dispatch order:

    1. **All-False fast path** — returns a single False run; no decode.
    2. **L3 direct RLE path** — used when run density is below
       :data:`_L3_DENSITY_THRESHOLD`; manipulates run lengths without
       allocating a 2D array.
    3. **cv2 fallback** — decodes to ``uint8``, calls
       ``cv2.resize(INTER_NEAREST)``, re-encodes; used for dense masks.

    Args:
        rle: int32 run-length array for the source crop.
        orig_h: Height of the source crop.
        orig_w: Width of the source crop.
        new_h: Target height.
        new_w: Target width.

    Returns:
        int32 RLE array for the resized crop.
    r   Nr(   r
   )interpolation)cv2r   r   rE   rF   r   maxrU   rS   r   resizer,   r-   INTER_NEARESTrN   r2   r   )r   orig_horig_wnew_hnew_wrZ   cropresizeds           r   _resize_croprd   U  s    8  ~xxrxx88 3x#a&)),AA3u== sFF3Djj		"((	''   fTl	 
 w''r   c                  2   e Zd ZdZdZ	 	 	 	 	 	 	 	 	 	 ddZe	 	 	 	 	 	 	 	 dd       ZddZddZ	ddZ
ddZedd	       Zedd
       Zedd       Zedd       Zed d       Zd!d"dZ	 	 	 	 d#dZd!d$dZd%dZed&d       Zd'dZ	 	 	 	 	 	 	 	 d(dZd)dZy)*CompactMasku  Memory-efficient crop-RLE mask storage for instance segmentation.

    Instead of storing N full ``(H, W)`` boolean arrays, :class:`CompactMask`
    encodes each mask as a run-length sequence of its bounding-box crop.  This
    reduces memory from O(N·H·W) to roughly O(N·bbox_area), which is orders of
    magnitude smaller for sparse masks on high-resolution images.

    The class exposes a duck-typed interface compatible with ``np.ndarray``
    masks used elsewhere in ``supervision``:

    * ``mask[int]`` → dense ``(H, W)`` bool array (annotators, converters).
    * ``mask[slice | list | ndarray]`` → new :class:`CompactMask` (filtering).
    * ``np.asarray(mask)`` → dense ``(N, H, W)`` bool array (numpy interop).
    * ``mask.shape``, ``mask.dtype``, ``mask.area`` — match the dense API.

    :class:`CompactMask` is **not** a drop-in ``np.ndarray`` replacement.
    When you need to call arbitrary ndarray methods (``astype``, ``reshape``,
    ``ravel``, ``any``, ``all``, …) call :meth:`to_dense` first:
    ``cm.to_dense().astype(np.uint8)``.  :meth:`to_dense` is the single
    explicit materialisation boundary.

    .. note:: **RLE encoding — COCO / pycocotools pixel-scan order**

        :class:`CompactMask` uses **column-major (Fortran-order, F-order)**
        run-lengths scoped to each mask's bounding-box crop, matching the
        pixel-scan order used by the COCO API (pycocotools).  The crop scope
        still differs from the full-image scope used by pycocotools, so a
        :class:`CompactMask` RLE cannot be passed directly to
        ``maskUtils.iou()`` or ``maskUtils.decode()`` without re-scoping to
        the full canvas.  Use :meth:`to_dense` to obtain a standard boolean
        array for pycocotools interop.

        This scan order is part of CompactMask's internal RLE representation.
        Switching from row-major (C-order) to column-major (F-order) is a
        backward-incompatible format change for any persisted or serialized
        :class:`CompactMask` state, including pickled objects and any
        external storage of ``._rles``.  Older stored RLE arrays will decode
        incorrectly under the new convention.

        Migration note: load or decode legacy masks with the older version,
        materialize them to dense boolean arrays, and then re-encode them
        with the current version (for example via :meth:`to_dense` followed
        by :meth:`from_dense`) before persisting them again.

    Args:
        rles: List of N int32 run-length arrays.
        crop_shapes: Array of shape ``(N, 2)`` — ``(crop_h, crop_w)`` per mask.
        offsets: Array of shape ``(N, 2)`` — ``(x1, y1)`` bounding-box origins.
        image_shape: ``(H, W)`` of the full image.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> from supervision.detection.compact_mask import CompactMask
        >>> masks = np.zeros((2, 100, 100), dtype=bool)
        >>> masks[0, 10:20, 10:20] = True
        >>> masks[1, 50:70, 50:80] = True
        >>> xyxy = np.array([[10, 10, 19, 19], [50, 50, 79, 69]], dtype=np.float32)
        >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(100, 100))
        >>> len(cm)
        2
        >>> cm.shape
        (2, 100, 100)

        ```
    )_crop_shapes_image_shape_offsets_rlesc                <    || _         || _        || _        || _        y N)rj   rg   ri   rh   )selfrlescrop_shapesoffsetsimage_shapes        r   __init__zCompactMask.__init__  s"     37
3>/6-8r   c                ,   |\  }}t        |      }|dk(  rQ | g t        j                  dt        j                        t        j                  dt        j                        |      S g }g }g }	t	        |      D ]E  }
||
   \  }}}}t        t        dt        t        |      |dz
                    }t        t        dt        t        |      |dz
                    }t        t        dt        t        |      |dz
                    }t        t        dt        t        |      |dz
                    }||k  s||k  r t        j                  dt              }||}}n||
||dz   ||dz   f   }||z
  dz   }||z
  dz   }|j                  t        |             |j                  ||f       |	j                  ||f       H t        j                  |t        j                        }t        j                  |	t        j                        } | ||||      S )at  Create a :class:`CompactMask` from a dense ``(N, H, W)`` bool array.

        Bounding boxes are clipped to image bounds and interpreted in the
        supervision ``xyxy`` convention (inclusive max coordinates). A
        box with invalid ordering (``x2 < x1`` or ``y2 < y1``) is replaced by
        a ``1x1`` all-False crop to avoid degenerate RLE.

        Args:
            masks: Dense boolean mask array of shape ``(N, H, W)``.
            xyxy: Bounding boxes of shape ``(N, 4)`` in ``[x1, y1, x2, y2]``
                format.
            image_shape: ``(H, W)`` of the full image.

        Returns:
            A new :class:`CompactMask` instance.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 100, 100), dtype=bool)
            >>> masks[0, 10:20, 10:20] = True
            >>> xyxy = np.array([[10, 10, 19, 19]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(100, 100))
            >>> cm.shape
            (1, 100, 100)

            ```
        r   r   r   r(   r
   r
   r
   )r   r   r*   rF   r   r   r[   r   zerosr2   r   r   rE   )clsmasksxyxyrq   img_himg_w	num_masksrn   crop_shapes_listoffsets_listmask_idxx1y1x2y2x1cy1cx2cy2crb   r   r   ro   rp   s                           r   
from_densezCompactMask.from_dense  s   H #uJ	>rxx0rxx0	  -/24.0i( 	,H!(^NBBc!SR%!)456Cc!SR%!)456Cc!SR%!)456Cc!SR%!)456C SyC#Ixxd3SXsS1W}cC!GmCD3Y]F3Y]FKK+D12##VV$45c
+'	,* hh/rxx@((<rxx84g{;;r   c                   t        | j                        }| j                  \  }}t        j                  |||ft
              }t        |      D ]  }t        | j                  |df         t        | j                  |df         }}t        | j                  |df         t        | j                  |df         }	}t        | j                  |   ||      }
|
|||	|	|z   |||z   f<    |S )aj  Materialise all masks as a dense ``(N, H, W)`` boolean array.

        Returns:
            Boolean array of shape ``(N, H, W)``.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 50, 50), dtype=bool)
            >>> masks[0, 10:20, 10:30] = True
            >>> xyxy = np.array([[10, 10, 29, 19]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(50, 50))
            >>> cm.to_dense().shape
            (1, 50, 50)

            ```
        r(   r   r
   )r   rj   rh   r   rv   r2   r   r   rg   ri   r   )rm   r|   rz   r{   resultr   r   r   r   r   rb   s              r   to_densezCompactMask.to_dense+  s    & 

O	((u(*)UE1JRV(Wi( 	HHD%%hk23D%%hk23 F x{34c$--RS:T6UB&tzz(';VVLDCGF8R"v+-rBK/??@	H r   c                    t        | j                  |df         }t        | j                  |df         }t        | j                  |   ||      S )u"  Decode a single mask crop without allocating the full image array.

        This is an O(crop_area) operation — ideal for annotators that only
        need the cropped region.

        Args:
            index: Index of the mask to decode.

        Returns:
            Boolean array of shape ``(crop_h, crop_w)``.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 100, 100), dtype=bool)
            >>> masks[0, 20:30, 10:40] = True
            >>> xyxy = np.array([[10, 20, 39, 29]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(100, 100))
            >>> cm.crop(0).shape
            (10, 30)

            ```
        r   r
   )r   rg   r   rj   )rm   indexr   r   s       r   rb   zCompactMask.cropK  sN    2 T&&uax01T&&uax01"4::e#4ffEEr   c                ,    t        | j                        S )a  Return the number of masks.

        Returns:
            Number of masks N.

        Examples:
            ```pycon
            >>> from supervision.detection.compact_mask import CompactMask
            >>> import numpy as np
            >>> cm = CompactMask(
            ...     [], np.empty((0, 2), dtype=np.int32),
            ...     np.empty((0, 2), dtype=np.int32), (100, 100))
            >>> len(cm)
            0

            ```
        )r   rj   rm   s    r   __len__zCompactMask.__len__l  s    $ 4::r   c              #  L   K   t        t        |             D ]	  }| |     yw)z6Iterate over masks as dense ``(H, W)`` boolean arrays.N)r   r   )rm   r   s     r   __iter__zCompactMask.__iter__  s(     c$i( 	!Hx. 	!s   "$c                <    | j                   \  }}t        |       ||fS )a  Return ``(N, H, W)`` matching the dense mask convention.

        Returns:
            Tuple ``(N, H, W)``.

        Examples:
            ```pycon
            >>> from supervision.detection.compact_mask import CompactMask
            >>> import numpy as np
            >>> cm = CompactMask(
            ...     [], np.empty((0, 2), dtype=np.int32),
            ...     np.empty((0, 2), dtype=np.int32), (480, 640))
            >>> cm.shape
            (0, 480, 640)

            ```
        )rh   r   )rm   rz   r{   s      r   shapezCompactMask.shape  s$    & ((uD	5%((r   c                    | j                   S )ak  Return per-mask crop origins as ``(x1, y1)`` integer offsets.

        Returns:
            Array of shape ``(N, 2)`` with ``int32`` offsets.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> masks[0, 2:4, 3:5] = True
            >>> xyxy = np.array([[3, 2, 4, 3]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> cm.offsets.tolist()
            [[3, 2]]

            ```
        )ri   r   s    r   rp   zCompactMask.offsets  s    ( }}r   c                   t        |       dk(  r%t        j                  dt        j                        S | j                  dddf   }| j                  dddf   }|| j
                  dddf   z   dz
  }|| j
                  dddf   z   dz
  }t        j                  ||||f      j                  t        j                  d      S )a  Return per-mask inclusive bounding boxes in ``xyxy`` format.

        Boxes are derived from crop metadata:
        ``x2 = x1 + crop_w - 1``, ``y2 = y1 + crop_h - 1``.

        Returns:
            Array of shape ``(N, 4)`` with ``int32`` boxes
            ``[x1, y1, x2, y2]``.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> masks[0, 2:5, 3:7] = True
            >>> xyxy = np.array([[3, 2, 6, 4]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> cm.bbox_xyxy.tolist()
            [[3, 2, 6, 4]]

            ```
        r   )r      r(   Nr
   F)copy)r   r   r*   rF   ri   rg   column_stackrN   )rm   r   r   r   r   s        r   	bbox_xyxyzCompactMask.bbox_xyxy  s    0 t9>88F"((33$(MM!Q$$7$(MM!Q$$7$&):):1a4)@$@1$D$&):):1a4)@$@1$DBB/077u7MMr   c                4    t        j                  t              S )u  Return ``np.dtype(bool)`` — always.

        Returns:
            ``np.dtype(bool)``.

        Examples:
            ```pycon
            >>> from supervision.detection.compact_mask import CompactMask
            >>> import numpy as np
            >>> cm = CompactMask(
            ...     [], np.empty((0, 2), dtype=np.int32),
            ...     np.empty((0, 2), dtype=np.int32), (100, 100))
            >>> cm.dtype
            dtype('bool')

            ```
        )r   r)   r2   r   s    r   r)   zCompactMask.dtype  s    & xx~r   c                    t        j                  | j                  D cg c]  }t        |       c}t         j                        S c c}w )a  Compute the area (``True`` pixel count) of each mask.

        Returns:
            int64 array of shape ``(N,)`` with per-mask pixel counts.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((2, 100, 100), dtype=bool)
            >>> masks[0, 0:10, 0:10] = True  # 100 pixels
            >>> masks[1, 0:5, 0:5] = True    # 25 pixels
            >>> xyxy = np.array([[0, 0, 9, 9], [0, 0, 4, 4]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(100, 100))
            >>> cm.area.tolist()
            [100, 25]

            ```
        r(   )r   rE   rj   r   r0   )rm   r   s     r   areazCompactMask.area  s/    * xx4::>C3>bhhOO>s   ANc                d    |dk(  r| j                   S | j                         j                  |      S )a"  NumPy-compatible sum with a fast path for per-mask area.

        When ``axis=(1, 2)``, returns the per-mask True-pixel count via
        :attr:`area` without materialising the full dense array.

        Args:
            axis: Axis or axes to sum over.

        Returns:
            Sum result matching NumPy semantics.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> masks[0, 0:3, 0:3] = True
            >>> xyxy = np.array([[0, 0, 2, 2]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> cm.sum(axis=(1, 2)).tolist()
            [9]

            ```
        )r
   r   axis)r   r   r   )rm   r   s     r   r   zCompactMask.sum  s/    2 6>99}}"""--r   c                   t        |t        t        j                  f      rt        |      }| j                  \  }}t        j
                  ||ft              }t        | j                  |df         }t        | j                  |df         }t        | j                  |df         }t        | j                  |df         }	t        | j                  |   ||      }
|
||	|	|z   |||z   f<   |S t        |t              r?t        | j                  |   | j                  |   | j                  |   | j                        S t        |t        j                        r,|j                  t        k(  rt        j                  |      d   }nt        |t               rAt#        d |D              r/t        j$                  t        j&                  |t                    }n.t        j&                  t!        |      t        j(                        }|D cg c]  }| j                  t        |          }}| j                  |   }| j                  |   }t        |||| j                        S c c}w )u8  Index into the mask collection.

        * ``int`` → dense ``(H, W)`` bool array (for annotators, iterators).
        * ``slice | list | ndarray`` → new :class:`CompactMask` (for filtering).

        Args:
            index: An integer returns a dense ``(H, W)`` mask.  Any other
                supported index type returns a new :class:`CompactMask`.

        Returns:
            Dense ``(H, W)`` ``np.ndarray`` for integer index, or a new
            :class:`CompactMask` for all other index types.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((3, 20, 20), dtype=bool)
            >>> xyxy = np.array(
            ...     [[0,0,5,5],[5,5,10,10],[10,10,15,15]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(20, 20))
            >>> cm[0].shape        # int → dense (H, W)
            (20, 20)
            >>> len(cm[[0, 2]])    # list → CompactMask
            2

            ```
        r(   r   r
   c              3  \   K   | ]$  }t        |t        t        j                  f       & y wrl   )
isinstancer2   r   r+   ).0items     r   	<genexpr>z*CompactMask.__getitem__.<locals>.<genexpr>S  s%      -
37JtdBHH-.-
s   *,)r   r   r   integerrh   rv   r2   rg   ri   r   rj   slicerf   ndarrayr)   wherelistallr.   asarrayintp)rm   r   idxrz   r{   r   r   r   r   r   rb   idx_arrr   new_rlesnew_crop_shapesnew_offsetss                   r   __getitem__zCompactMask.__getitem__  s   @ ec2::./e*C,,LE5,.HHeU^4,PF**3623F**3623FT]]36*+BT]]36*+B&tzz#GD9=F2V#R"v+%556M eU#

5!!!%(e$!!	  eRZZ(U[[D-@hhuoa(Gt$ -
;@-
 *
 nnRZZT%BCGjjeBGG<G>EF(DJJs8}-FF151B1B71K-1]]7-C8_k4CTCTUU Gs   Ic                L    | j                         }||j                  |      S |S )a  NumPy interop: materialise as a dense ``(N, H, W)`` array.

        Called by ``np.asarray(compact_mask)`` and similar NumPy functions.

        Args:
            dtype: Optional dtype to cast the result to.

        Returns:
            Dense boolean array of shape ``(N, H, W)``.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> xyxy = np.array([[0, 0, 5, 5]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> np.asarray(cm).shape
            (1, 10, 10)

            ```
        )r   rN   )rm   r)   r   s      r   	__array__zCompactMask.__array___  s)    . ==''r   c                2   t        |t              r;t        t        j                  | j                         |j                                     S t        |t        j                        r-t        t        j                  | j                         |            S t        S )a  Element-wise equality with another :class:`CompactMask` or ndarray.

        Args:
            other: Another :class:`CompactMask` or ``np.ndarray``.

        Returns:
            ``True`` if all masks are pixel-identical.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> xyxy = np.array([[0, 0, 5, 5]], dtype=np.float32)
            >>> cm1 = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> cm2 = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> cm1 == cm2
            True

            ```
        )r   rf   r2   r   array_equalr   r   NotImplemented)rm   others     r   __eq__zCompactMask.__eq__{  sa    , e[)t}}8HIJJeRZZ(t}}>??r   c                   | st        d      | d   j                  }| dd D ],  }|j                  |k7  st        d| d|j                          g }| D ]  }|j                  |j                          t	        j
                  | D cg c]  }|j                   c}d      }t	        j
                  | D cg c]  }|j                   c}d      }t        ||||      S c c}w c c}w )af  Concatenate multiple :class:`CompactMask` objects into one.

        All inputs must have the same ``image_shape``.

        Args:
            masks_list: Non-empty list of :class:`CompactMask` objects.

        Returns:
            A new :class:`CompactMask` containing every mask from the inputs,
            in order.

        Raises:
            ValueError: If ``masks_list`` is empty or image shapes differ.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks1 = np.zeros((2, 50, 50), dtype=bool)
            >>> masks2 = np.zeros((3, 50, 50), dtype=bool)
            >>> xyxy1 = np.array([[0,0,10,10],[10,10,20,20]], dtype=np.float32)
            >>> xyxy2 = np.array(
            ...     [[0,0,5,5],[5,5,10,10],[10,10,15,15]], dtype=np.float32)
            >>> cm1 = CompactMask.from_dense(masks1, xyxy1, image_shape=(50, 50))
            >>> cm2 = CompactMask.from_dense(masks2, xyxy2, image_shape=(50, 50))
            >>> len(CompactMask.merge([cm1, cm2]))
            5

            ```
        z2Cannot merge an empty list of CompactMask objects.r   r
   Nz>Cannot merge CompactMask objects with different image shapes: z vs r   )	
ValueErrorrh   rD   rj   r   concatenaterg   ri   rf   )
masks_listrq   cmr   r   r   s         r   mergezCompactMask.merge  s    @ QRR m00QR. 	B+- T"m4'8: 	 13 	&BOOBHH%	&
 24'12R__22
 .0^^#-.RR[[.Q.
 8_k;OO 3 /s   C!7C&c           	     *   t        | j                        }|dk(  r^t        g t        j                  dt        j
                        t        j                  dt        j
                        | j                        S g }g }g }t        |      D ]  }| j                  |      }t        | j                  |df         }t        | j                  |df         }t        j                  |d      }	t        j                  |d      }
|	j                         sX|j                  t        t        j                  dt                           |j                  d       |j                  ||f       t        j                   |	      d   }t        j                   |
      d   }t        |d         t        |d         }}t        |d         t        |d         }}|||dz   ||dz   f   }|j                  t        |             |j                  ||z
  dz   ||z
  dz   f       |j                  ||z   ||z   f        t        |t        j"                  |t        j
                        t        j"                  |t        j
                        | j                        S )u  Re-encode all masks using tight bounding boxes.

        When the original ``xyxy`` boxes are padded or loose — common with
        object-detector outputs and full-image boxes used in tests — each RLE
        crop encodes more background (``False``) pixels than necessary.  This
        method decodes every crop, trims it to the minimal rectangle that
        contains all ``True`` pixels, and re-encodes.  All-``False`` masks are
        normalised to a ``1x1`` all-``False`` crop.

        The call is O(sum of crop areas) — suitable as a one-time cleanup
        after accumulating many merges (e.g. after
        :class:`~supervision.detection.tools.inference_slicer.InferenceSlicer`
        tiles are merged).

        Returns:
            A new :class:`CompactMask` with minimal-area crops and updated
            offsets.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 10, 10), dtype=bool)
            >>> masks[0, 3:7, 3:7] = True
            >>> # Deliberately loose bbox: covers the full image.
            >>> xyxy = np.array([[0, 0, 9, 9]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(10, 10))
            >>> repacked = cm.repack()
            >>> repacked.offsets.tolist()  # tight origin: x1=3, y1=3
            [[3, 3]]

            ```
        r   rt   r(   r
   r   ru   r   )r   rj   rf   r   r*   rF   rh   r   rb   r   ri   anyr   r   rv   r2   r   rE   )rm   r|   r   new_crop_shapes_listnew_offsets_listr   rb   x1_offy1_offrows_anycols_any	y_indices	x_indicesy_miny_maxx_minx_maxtights                     r   repackzCompactMask.repack  s5   D 

O	>rxx0rxx0!!	  136824i( 	FH99X&Dx{34Fx{34Fvvd+Hvvd+H<<> 3BHHV44P QR$++F3 ''(89*1-I*1-Iy|,c)B-.@5Ey|,c)B-.@5E*EEAI,==>EOO/67 ''):EEMA<M(NO##Ve^Ve^$DE/	F2 HH):HH%RXX6	
 	
r   c           	     t   |\  }}|dk  s|dk  rt        d      t        |       }|dk(  rTt        g t        j                  dt        j
                        t        j                  dt        j
                        |      S | j                  t        j                  ||gt        j
                        z   }|dddf   }|dddf   }	|| j                  dddf   z   dz
  }
|	| j                  dddf   z   dz
  }|dk  |	dk  z  |
|k\  z  ||k\  z  }|j                         s9t        t        | j                        | j                  j                         ||      S g }g }g }t        |      D ]  }t        ||         }t        |	|         }t        |
|         }t        ||         }||   su|j                  | j                  |          |j                  t        | j                  |df         t        | j                  |df         f       |j                  ||f       t!        d|      }t!        d|      }t#        |dz
  |      }t#        |dz
  |      }||kD  s||kD  rt#        t!        |d      |dz
        }t#        t!        |d      |dz
        }|j                  t%        t        j&                  dt(                           |j                  d       |j                  ||f       | j+                  |      }|||z
  ||z
  dz   ||z
  ||z
  dz   f   }|j                  t%        |             |j                  ||z
  dz   ||z
  dz   f       |j                  ||f        t        |t        j                  |t        j
                        t        j                  |t        j
                        |      S )a  Return a new :class:`CompactMask` with adjusted offsets and image shape.

        Used by :class:`~supervision.detection.tools.inference_slicer.InferenceSlicer`
        to relocate tile-local masks into full-image coordinates without
        materialising the dense ``(N, H, W)`` array.

        Args:
            dx: Pixels to add to every mask's ``x1`` offset.
            dy: Pixels to add to every mask's ``y1`` offset.
            new_image_shape: ``(H, W)`` of the full (destination) image.

        Returns:
            New :class:`CompactMask` with updated offsets and image shape.
            Crops are clipped to stay inside ``new_image_shape``; masks fully
            outside are represented as ``1x1`` all-False crops.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 20, 20), dtype=bool)
            >>> xyxy = np.array([[5, 5, 15, 15]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(20, 20))
            >>> cm2 = cm.with_offset(100, 200, new_image_shape=(400, 400))
            >>> cm2.offsets[0].tolist()
            [105, 205]

            ```
        r   0new_image_shape must contain positive dimensionsrt   r(   Nr
   ru   )r   r   rf   r   r*   rF   ri   rE   rg   r   r   rj   r   r   r   r   r[   r   r   rv   r2   rb   )rm   dxdynew_image_shaper`   ra   r|   r   x1sy1sx2sy2s
needs_clipout_rlesout_crop_shapesout_offsets_listr   r   r   r   r   ix1iy1ix2iy2anchor_xanchor_yrb   clippeds                                r   with_offsetzCompactMask.with_offset+  s   F 'uA:!OPPI	>rxx0rxx0	  .2]]RXXHBHH>
 .
 !Q$!Q$D%%ad++a/D%%ad++a/ 1Wq!SE\2cUlC 	 ~~TZZ !!&&(	  131324i( "	0HS]#BS]#BS]#BS]#Bh'

8 45&&D--hk:;D--hk:; !''R1a*Ca*Ceai$Ceai$CSyC#Is2qz5195s2qz5195 3BHHV44P QR&&v. ''8(<=99X&D38cBhl2C"HsRx!|4KKLGOO/89""C#IM39q=#AB##S#J/E"	0H HH_BHH5HH%RXX6	
 	
r   c                	   ddl m} |\  }}|dk  s|dk  rt        d      ||f| j                  k(  rQt	        t        | j                        | j                  j                         | j                  j                         |      S t        |       dk(  rTt	        g t        j                  dt        j                        t        j                  dt        j                        |      S | j                  \  }}||z  }||z  }| j                  dddf   j                  t        j                        }	| j                  dddf   j                  t        j                        }
|	| j                  dddf   z   dz
  }|
| j                  dddf   z   dz
  }t        j                   t        j"                  |	|z        d|dz
        j                  t        j                        }t        j                   t        j"                  |
|z        d|dz
        j                  t        j                        }t        j                   t        j"                  ||z        d|dz
        j                  t        j                        }t        j                   t        j"                  ||z        d|dz
        j                  t        j                        }t        j$                  d||z
  dz         j                  t        j                        }t        j$                  d||z
  dz         j                  t        j                        }| j                  dddf   }| j                  dddf   }t'        t        |             D cg c]F  }| j                  |   t)        ||         t)        ||         t)        ||         t)        ||         fH }}t        |       }|t*        k\  rO |t-        |t/        j0                         xs d      	      5 }t        |j3                  d
 |            }ddd       n|D cg c]
  }t5        |  }}t        j6                  ||f      j                  t        j                        }t        j6                  ||f      j                  t        j                        }t	        |||      S c c}w # 1 sw Y   xY wc c}w )u4  Return a new CompactMask scaled to a different image resolution.

        Each crop mask is resized with nearest-neighbour interpolation.
        Sparse masks use direct RLE arithmetic (:func:`_rle_resize`); dense
        masks fall back to ``cv2.resize(INTER_NEAREST)``.  Offsets and crop
        dimensions are scaled proportionally to the new image size.

        Performance notes:

        * Coordinate arithmetic is fully vectorised (no Python loop over N).
        * All-``False`` crops skip decode/resize entirely.
        * For N >= 8, resize runs in a thread pool — NumPy and OpenCV
          release the GIL so crops execute in parallel on multi-core CPUs.

        Args:
            new_image_shape: ``(H, W)`` of the target image.

        Returns:
            New :class:`CompactMask` with updated ``image_shape``, scaled
            offsets, scaled crop shapes, and re-encoded RLE crops.

        Raises:
            ValueError: If any dimension in *new_image_shape* is ``<= 0``.

        Examples:
            ```pycon
            >>> import numpy as np
            >>> from supervision.detection.compact_mask import CompactMask
            >>> masks = np.zeros((1, 100, 100), dtype=bool)
            >>> masks[0, 20:40, 30:60] = True
            >>> xyxy = np.array([[30, 20, 59, 39]], dtype=np.float32)
            >>> cm = CompactMask.from_dense(masks, xyxy, image_shape=(100, 100))
            >>> small = cm.resize((50, 50))
            >>> small.shape
            (1, 50, 50)
            >>> small.offsets[0].tolist()
            [15, 10]

            ```
        r   )ThreadPoolExecutorr   rt   r(   Nr
   r   )max_workersc                    t        |  S rl   )rd   )as    r   <lambda>z$CompactMask.resize.<locals>.<lambda>  s    |Q'7 r   )concurrent.futuresr   r   rh   rf   r   rj   rg   r   ri   r   r   r*   rF   rN   float64cliproundmaximumr   r   rW   r   os	cpu_countmaprd   r   )rm   r   r   r`   ra   rz   r{   sxsyr   r   r   r   new_x1snew_y1snew_x2snew_y2snew_crop_wsnew_crop_hsorig_crop_hsorig_crop_wsiargsnpoolr   r   r   r   s                                r   r\   zCompactMask.resize  s   R 	:&uA:!OPP
 5>T...TZZ !!&&(""$	  t9>rxx0rxx0	  ((uU]U] mmAqD!((4mmAqD!((4D%%ad++a/D%%ad++a/''"((38,a;BB288L''"((38,a;BB288L''"((38,a;BB288L''"((38,a;BB288L-/ZZw 1$.

&
 	 .0ZZw 1$.

&
 	
 ((A.((A. 3t9%	
  

1LO$LO$KN#KN#	
 	
 I###Ar||~7J0KL PT8<HH7>9 
 377Qa(7H7//;*DELLRXXVoow&89@@J8_k?SS-	
 
 8s   2AR-<R2&R>2R;)
rn   zlist[npt.NDArray[np.int32]]ro   npt.NDArray[np.int32]rp   r  rq   tuple[int, int]returnNone)rx   npt.NDArray[np.bool_]ry   npt.NDArray[Any]rq   r  r  rf   )r  r  )r   r   r  r  )r  r   )r  zIterator[npt.NDArray[np.bool_]])r  ztuple[int, int, int])r  r  )r  znp.dtype[Any])r  znpt.NDArray[np.int64]rl   )r   zint | tuple[int, ...] | Noner  znpt.NDArray[Any] | int)r   z*int | slice | list[Any] | npt.NDArray[Any]r  z#npt.NDArray[np.bool_] | CompactMask)r)   znp.dtype[Any] | Noner  r  )r   objectr  r2   )r   zlist[CompactMask]r  rf   )r  rf   )r   r   r   r   r   r  r  rf   )r   r  r  rf   )__name__
__module____qualname____doc__	__slots__rr   classmethodr   r   rb   r   r   propertyr   rp   r   r)   r   r   r   r   r   staticmethodr   r   r   r\    r   r   rf   rf     s   AF FI
9)
9 +
9 '	
9
 %
9 

9  I<$I< I< %	I<
 
I< I<^@FB(!
 ) )*  * N N@  ( P P,.:BV9BV 
-BVH8@ :P :PxM
fw
w
 w
 )	w

 
w
zrTr   rf   )r   r  r  r   )r   r  r   r   r   r   r  list[list[int]])r4   	list[int]r5   r   r6   r  r  r  )rG   r  rH   r   r  r  )r   r  r   r   r   r   r7   r   rO   r   r  r  )r   r  r^   r   r_   r   r`   r   ra   r   r  r  )r  
__future__r   r   collections.abcr   typingr   numpyr   numpy.typingnpt&supervision.detection.utils.convertersr   r   r   r&   rB   rK   rS   rU   __annotations__rW   rd   rf   r  r   r   <module>r     sN  	 # 	 $   ",L	LL L 	L^>>> #> 	>B5Q 5Q5Q 5QpQ2	Q2Q2 Q2 	Q2
 Q2 Q2n  $ u # S -(	-(-( -( 	-(
 -( -(`UT UTr   