Skip to content

Converters Utils

supervision.detection.utils.converters.xyxy_to_xywh(xyxy: npt.NDArray[np.number]) -> npt.NDArray[np.number]

Converts bounding box coordinates from (x_min, y_min, x_max, y_max) format to (x, y, width, height) format.

Parameters:

Name Type Description Default

xyxy

NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (x_min, y_min, x_max, y_max).

required

Returns:

Type Description
NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (x, y, width, height).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> xyxy = np.array([
...     [10, 20, 40, 60],
...     [15, 25, 50, 70]
... ])
>>> sv.xyxy_to_xywh(xyxy=xyxy)
array([[10, 20, 30, 40],
       [15, 25, 35, 45]])
Source code in src/supervision/detection/utils/converters.py
def xyxy_to_xywh(xyxy: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
    """Converts bounding box coordinates from `(x_min, y_min, x_max, y_max)` format to
    `(x, y, width, height)` format.

    Args:
        xyxy: A numpy array of shape `(N, 4)` where each row
            corresponds to a bounding box in the format `(x_min, y_min, x_max,
            y_max)`.

    Returns:
        A numpy array of shape `(N, 4)` where each row corresponds
            to a bounding box in the format `(x, y, width, height)`.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> xyxy = np.array([
        ...     [10, 20, 40, 60],
        ...     [15, 25, 50, 70]
        ... ])
        >>> sv.xyxy_to_xywh(xyxy=xyxy)
        array([[10, 20, 30, 40],
               [15, 25, 35, 45]])

        ```
    """
    xywh = xyxy.copy()
    xywh[:, 2] = xyxy[:, 2] - xyxy[:, 0]
    xywh[:, 3] = xyxy[:, 3] - xyxy[:, 1]
    return cast(npt.NDArray[np.number], np.asarray(xywh))

supervision.detection.utils.converters.xywh_to_xyxy(xywh: npt.NDArray[np.number]) -> npt.NDArray[np.number]

Converts bounding box coordinates from (x, y, width, height) format to (x_min, y_min, x_max, y_max) format.

Parameters:

Name Type Description Default

xywh

NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (x, y, width, height).

required

Returns:

Type Description
NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (x_min, y_min, x_max, y_max).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> xywh = np.array([
...     [10, 20, 30, 40],
...     [15, 25, 35, 45]
... ])
>>> sv.xywh_to_xyxy(xywh=xywh)
array([[10, 20, 40, 60],
       [15, 25, 50, 70]])
Source code in src/supervision/detection/utils/converters.py
def xywh_to_xyxy(xywh: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
    """Converts bounding box coordinates from `(x, y, width, height)` format to `(x_min,
    y_min, x_max, y_max)` format.

    Args:
        xywh: A numpy array of shape `(N, 4)` where each row
            corresponds to a bounding box in the format `(x, y, width, height)`.

    Returns:
        A numpy array of shape `(N, 4)` where each row corresponds
            to a bounding box in the format `(x_min, y_min, x_max, y_max)`.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> xywh = np.array([
        ...     [10, 20, 30, 40],
        ...     [15, 25, 35, 45]
        ... ])
        >>> sv.xywh_to_xyxy(xywh=xywh)
        array([[10, 20, 40, 60],
               [15, 25, 50, 70]])

        ```
    """
    xyxy = xywh.copy()
    xyxy[:, 2] = xywh[:, 0] + xywh[:, 2]
    xyxy[:, 3] = xywh[:, 1] + xywh[:, 3]
    return cast(npt.NDArray[np.number], np.asarray(xyxy))

supervision.detection.utils.converters.xyxy_to_xcycarh(xyxy: npt.NDArray[np.number]) -> npt.NDArray[np.floating]

Converts bounding box coordinates from (x_min, y_min, x_max, y_max) into measurement space to format (center x, center y, aspect ratio, height), where the aspect ratio is width / height.

Parameters:

Name Type Description Default

xyxy

NDArray[number]

Bounding box in format (x1, y1, x2, y2). Expected shape is (N, 4).

required

Returns: Bounding box in format (center x, center y, aspect ratio, height). Shape (N, 4).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> xyxy = np.array([
...     [10, 20, 40, 60],
...     [15, 25, 50, 70]
... ])
>>> sv.xyxy_to_xcycarh(xyxy=xyxy)  # doctest: +ELLIPSIS
array([[25.        , 40.        ,  0.75      , 40.        ],
       [32.5       , 47.5       ,  0.77..., 45.        ]])
Source code in src/supervision/detection/utils/converters.py
def xyxy_to_xcycarh(xyxy: npt.NDArray[np.number]) -> npt.NDArray[np.floating]:
    """Converts bounding box coordinates from `(x_min, y_min, x_max, y_max)` into
    measurement space to format `(center x, center y, aspect ratio, height)`, where the
    aspect ratio is `width / height`.

    Args:
        xyxy: Bounding box in format `(x1, y1, x2, y2)`.
            Expected shape is `(N, 4)`.
    Returns:
        Bounding box in format
            `(center x, center y, aspect ratio, height)`. Shape `(N, 4)`.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> xyxy = np.array([
        ...     [10, 20, 40, 60],
        ...     [15, 25, 50, 70]
        ... ])
        >>> sv.xyxy_to_xcycarh(xyxy=xyxy)  # doctest: +ELLIPSIS
        array([[25.        , 40.        ,  0.75      , 40.        ],
               [32.5       , 47.5       ,  0.77..., 45.        ]])

        ```
    """
    if xyxy.size == 0:
        return np.empty((0, 4), dtype=float)

    x1, y1, x2, y2 = xyxy.T
    width = x2 - x1
    height = y2 - y1
    center_x = x1 + width / 2
    center_y = y1 + height / 2

    aspect_ratio = np.divide(
        width,
        height,
        out=np.zeros_like(width, dtype=float),
        where=height != 0,
    )
    result = np.column_stack((center_x, center_y, aspect_ratio, height))
    return result.astype(float)

supervision.detection.utils.converters.xcycwh_to_xyxy(xcycwh: npt.NDArray[np.number]) -> npt.NDArray[np.number]

Converts bounding box coordinates from (center_x, center_y, width, height) format to (x_min, y_min, x_max, y_max) format.

Parameters:

Name Type Description Default

xcycwh

NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (center_x, center_y, width, height).

required

Returns:

Type Description
NDArray[number]

A numpy array of shape (N, 4) where each row corresponds to a bounding box in the format (x_min, y_min, x_max, y_max).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> xcycwh = np.array([
...     [50.0, 50.0, 20.0, 30.0],
...     [30.0, 40.0, 10.0, 15.0]
... ])
>>> sv.xcycwh_to_xyxy(xcycwh=xcycwh)
array([[40. , 35. , 60. , 65. ],
       [25. , 32.5, 35. , 47.5]])
Source code in src/supervision/detection/utils/converters.py
def xcycwh_to_xyxy(xcycwh: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
    """Converts bounding box coordinates from `(center_x, center_y, width, height)`
    format to `(x_min, y_min, x_max, y_max)` format.

    Args:
        xcycwh: A numpy array of shape `(N, 4)` where each row
            corresponds to a bounding box in the format `(center_x, center_y, width,
            height)`.

    Returns:
        A numpy array of shape `(N, 4)` where each row corresponds
            to a bounding box in the format `(x_min, y_min, x_max, y_max)`.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> xcycwh = np.array([
        ...     [50.0, 50.0, 20.0, 30.0],
        ...     [30.0, 40.0, 10.0, 15.0]
        ... ])
        >>> sv.xcycwh_to_xyxy(xcycwh=xcycwh)
        array([[40. , 35. , 60. , 65. ],
               [25. , 32.5, 35. , 47.5]])

        ```
    """
    # Build the result from the half-extent arithmetic instead of writing into a
    # copy of the input. Half of an odd width or height is fractional, so an
    # integer input array would silently truncate those coordinates; letting the
    # division set the output dtype keeps them exact.
    center_x = xcycwh[:, 0]
    center_y = xcycwh[:, 1]
    half_width = xcycwh[:, 2] / 2
    half_height = xcycwh[:, 3] / 2
    xyxy = np.column_stack(
        (
            center_x - half_width,
            center_y - half_height,
            center_x + half_width,
            center_y + half_height,
        )
    )
    return cast(npt.NDArray[np.number], xyxy)

supervision.detection.utils.converters.xyxy_to_polygons(box: npt.NDArray[np.number]) -> npt.NDArray[np.number]

Convert an array of boxes to an array of polygons. Retains the input datatype.

Parameters:

Name Type Description Default

box

NDArray[number]

An array of boxes (N, 4), where each box is represented as a list of four coordinates in the format (x_min, y_min, x_max, y_max).

required

Returns:

Type Description
NDArray[number]

An array of polygons (N, 4, 2), where each polygon is represented as a list of four coordinates in the format (x, y).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> box = np.array([[10, 20, 30, 40]])
>>> sv.xyxy_to_polygons(box)
array([[[10, 20],
        [30, 20],
        [30, 40],
        [10, 40]]])
Source code in src/supervision/detection/utils/converters.py
def xyxy_to_polygons(box: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
    """Convert an array of boxes to an array of polygons. Retains the input datatype.

    Args:
        box: An array of boxes (N, 4), where each box is represented as a
            list of four coordinates in the format `(x_min, y_min, x_max, y_max)`.

    Returns:
        An array of polygons (N, 4, 2), where each polygon is
            represented as a list of four coordinates in the format `(x, y)`.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> box = np.array([[10, 20, 30, 40]])
        >>> sv.xyxy_to_polygons(box)
        array([[[10, 20],
                [30, 20],
                [30, 40],
                [10, 40]]])

        ```
    """
    polygon = np.zeros((box.shape[0], 4, 2), dtype=box.dtype)
    polygon[:, :, 0] = box[:, [0, 2, 2, 0]]
    polygon[:, :, 1] = box[:, [1, 1, 3, 3]]
    return polygon

supervision.detection.utils.converters.mask_to_xyxy(masks: npt.NDArray[np.bool_], coordinate_convention: CoordinateConvention = 'inclusive') -> npt.NDArray[np.int_]

Converts a 3D np.array of 2D bool masks into a 2D np.array of bounding boxes.

Parameters:

Name Type Description Default

masks

NDArray[bool_]

A 3D np.array of shape (N, H, W) containing 2D bool masks.

required

coordinate_convention

CoordinateConvention

How to interpret the returned max corner. Use "inclusive" to preserve the legacy Supervision behavior, where x_max and y_max are the last covered pixel. Use "exclusive" for half-open boxes that match area and IoU arithmetic.

'inclusive'

Returns:

Type Description
NDArray[int_]

A 2D np.array of shape (N, 4) containing the bounding boxes (x_min, y_min, x_max, y_max) for each mask.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> masks = np.array([
...     [[False, False, False],
...      [False,  True,  True],
...      [False,  True,  True]],
... ])
>>> sv.mask_to_xyxy(masks)
array([[1, 1, 2, 2]])
Source code in src/supervision/detection/utils/converters.py
def mask_to_xyxy(
    masks: npt.NDArray[np.bool_],
    coordinate_convention: CoordinateConvention = "inclusive",
) -> npt.NDArray[np.int_]:
    """Converts a 3D `np.array` of 2D bool masks into a 2D `np.array` of bounding boxes.

    Args:
        masks: A 3D `np.array` of shape `(N, H, W)` containing 2D bool masks.
        coordinate_convention: How to interpret the returned max corner.
            Use `"inclusive"` to preserve the legacy Supervision behavior,
            where `x_max` and `y_max` are the last covered pixel. Use
            `"exclusive"` for half-open boxes that match area and IoU arithmetic.

    Returns:
        A 2D `np.array` of shape `(N, 4)` containing the bounding boxes
            `(x_min, y_min, x_max, y_max)` for each mask.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> masks = np.array([
        ...     [[False, False, False],
        ...      [False,  True,  True],
        ...      [False,  True,  True]],
        ... ])
        >>> sv.mask_to_xyxy(masks)
        array([[1, 1, 2, 2]])

        ```
    """
    n, height, width = masks.shape
    if masks.size == 0:
        return np.zeros((n, 4), dtype=int)

    # Reduce the mask stack to per-row / per-column occupancy, then read the
    # tight bounds straight off those 1D profiles instead of scanning every
    # pixel of every mask with `np.where`.
    rows_any = cast(npt.NDArray[np.bool_], masks.any(axis=2))  # (N, H)
    cols_any = cast(npt.NDArray[np.bool_], masks.any(axis=1))  # (N, W)

    x_min = cols_any.argmax(axis=1)
    y_min = rows_any.argmax(axis=1)

    if coordinate_convention == "inclusive":
        x_max = width - 1 - cols_any[:, ::-1].argmax(axis=1)
        y_max = height - 1 - rows_any[:, ::-1].argmax(axis=1)
    elif coordinate_convention == "exclusive":
        x_max = width - cols_any[:, ::-1].argmax(axis=1)
        y_max = height - rows_any[:, ::-1].argmax(axis=1)
    else:
        raise ValueError(
            "coordinate_convention must be 'inclusive' or 'exclusive', "
            f"got {coordinate_convention!r}."
        )

    xyxy = np.stack((x_min, y_min, x_max, y_max), axis=1).astype(int)
    # Empty masks have no bounds; keep the original all-zeros box for them.
    xyxy[~rows_any.any(axis=1)] = 0
    return xyxy

supervision.detection.utils.converters.mask_to_polygons(mask: npt.NDArray[np.bool_]) -> list[npt.NDArray[np.int32]]

Converts a binary mask to a list of polygons.

Parameters:

Name Type Description Default

mask

NDArray[bool_]

A binary mask represented as a 2D NumPy array of shape (H, W), where H and W are the height and width of the mask, respectively.

required

Returns:

Type Description
list[NDArray[int32]]

A list of polygons, where each polygon is represented by a NumPy array of shape (N, 2), containing the x, y coordinates of the points. Polygons with fewer points than MIN_POLYGON_POINT_COUNT = 3 are excluded from the output.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> mask = np.zeros((10, 10), dtype=bool)
>>> mask[2:6, 2:6] = True
>>> sv.mask_to_polygons(mask)
[array([[2, 2],
       [2, 5],
       [5, 5],
       [5, 2]], dtype=int32)]
Source code in src/supervision/detection/utils/converters.py
def mask_to_polygons(mask: npt.NDArray[np.bool_]) -> list[npt.NDArray[np.int32]]:
    """Converts a binary mask to a list of polygons.

    Args:
        mask: A binary mask represented as a 2D NumPy array of shape `(H, W)`,
            where H and W are the height and width of the mask, respectively.

    Returns:
        A list of polygons, where each polygon is represented by a NumPy array
            of shape `(N, 2)`, containing the `x`, `y` coordinates of the
            points. Polygons with fewer points than `MIN_POLYGON_POINT_COUNT = 3`
            are excluded from the output.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> mask = np.zeros((10, 10), dtype=bool)
        >>> mask[2:6, 2:6] = True
        >>> sv.mask_to_polygons(mask)
        [array([[2, 2],
               [2, 5],
               [5, 5],
               [5, 2]], dtype=int32)]

        ```
    """

    contours = cv2.find_contours(mask.astype(np.uint8))
    return [
        np.squeeze(contour, axis=1)
        for contour in contours
        if contour.shape[0] >= MIN_POLYGON_POINT_COUNT
    ]

supervision.detection.utils.converters.polygon_to_mask(polygon: npt.NDArray[np.number] | Sequence[Sequence[float]], resolution_wh: tuple[int, int]) -> npt.NDArray[np.uint8]

Generate a mask from a polygon.

Parameters:

Name Type Description Default

polygon

NDArray[number] | Sequence[Sequence[float]]

The polygon for which the mask should be generated, given as an (N, 2) array or a list of [x, y] numeric vertices. An empty polygon, or one with fewer than MIN_POLYGON_POINT_COUNT vertices, produces an all-zero mask.

required

resolution_wh

tuple[int, int]

The width and height of the desired resolution.

required

Returns:

Type Description
NDArray[uint8]

The generated 2D mask, where the polygon is marked with 1s and the rest is filled with 0s.

Raises:

Type Description
ValueError

If polygon does not have shape (N, 2) with numeric coordinates.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> polygon = np.array([[2, 2], [6, 2], [6, 6], [2, 6]])
>>> mask = sv.polygon_to_mask(polygon, resolution_wh=(10, 10))
>>> int(mask.sum())
25

>>> vertices = [[2, 2], [6, 2], [6, 6], [2, 6]]
>>> int(sv.polygon_to_mask(vertices, resolution_wh=(10, 10)).sum())
25

>>> int(sv.polygon_to_mask([], resolution_wh=(4, 4)).sum())
0
Source code in src/supervision/detection/utils/converters.py
def polygon_to_mask(
    polygon: npt.NDArray[np.number] | Sequence[Sequence[float]],
    resolution_wh: tuple[int, int],
) -> npt.NDArray[np.uint8]:
    """Generate a mask from a polygon.

    Args:
        polygon: The polygon for which the mask should be generated, given as
            an `(N, 2)` array or a list of `[x, y]` numeric vertices. An empty
            polygon, or one with fewer than `MIN_POLYGON_POINT_COUNT` vertices,
            produces an all-zero mask.
        resolution_wh: The width and height of the desired resolution.

    Returns:
        The generated 2D mask, where the polygon is marked with
            `1`s and the rest is filled with `0`s.

    Raises:
        ValueError: If `polygon` does not have shape `(N, 2)` with numeric
            coordinates.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> polygon = np.array([[2, 2], [6, 2], [6, 6], [2, 6]])
        >>> mask = sv.polygon_to_mask(polygon, resolution_wh=(10, 10))
        >>> int(mask.sum())
        25

        >>> vertices = [[2, 2], [6, 2], [6, 6], [2, 6]]
        >>> int(sv.polygon_to_mask(vertices, resolution_wh=(10, 10)).sum())
        25

        >>> int(sv.polygon_to_mask([], resolution_wh=(4, 4)).sum())
        0

        ```
    """
    polygon_arr = np.asarray(polygon)
    width, height = map(int, resolution_wh)
    mask = np.zeros((height, width), dtype=np.uint8)
    if polygon_arr.size == 0:
        return mask
    if (
        polygon_arr.ndim != 2
        or polygon_arr.shape[1] != 2
        or polygon_arr.dtype.kind not in _NUMERIC_DTYPE_KINDS
    ):
        raise ValueError(
            "Polygon must have shape (N, 2) with numeric coordinates; got "
            f"shape {polygon_arr.shape} and dtype {polygon_arr.dtype}."
        )
    if len(polygon_arr) < MIN_POLYGON_POINT_COUNT:
        return mask
    cv2.fillPoly(mask, [polygon_arr.astype(np.int32)], color=(1,))
    return mask

supervision.detection.utils.converters.polygon_to_xyxy(polygon: npt.NDArray[np.number]) -> npt.NDArray[np.number]

Converts a polygon represented by a NumPy array into a bounding box.

Parameters:

Name Type Description Default

polygon

NDArray[number]

A polygon represented by a NumPy array of shape (N, 2), containing the x, y coordinates of the points.

required

Returns:

Type Description
NDArray[number]

A 1D NumPy array containing the bounding box (x_min, y_min, x_max, y_max) of the input polygon.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> polygon = np.array([[10, 20], [30, 20], [30, 40], [10, 40]])
>>> sv.polygon_to_xyxy(polygon)
array([10, 20, 30, 40])
Source code in src/supervision/detection/utils/converters.py
def polygon_to_xyxy(polygon: npt.NDArray[np.number]) -> npt.NDArray[np.number]:
    """Converts a polygon represented by a NumPy array into a bounding box.

    Args:
        polygon: A polygon represented by a NumPy array of shape `(N, 2)`,
            containing the `x`, `y` coordinates of the points.

    Returns:
        A 1D NumPy array containing the bounding box
            `(x_min, y_min, x_max, y_max)` of the input polygon.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> polygon = np.array([[10, 20], [30, 20], [30, 40], [10, 40]])
        >>> sv.polygon_to_xyxy(polygon)
        array([10, 20, 30, 40])

        ```
    """
    x_min, y_min = np.min(polygon, axis=0)
    x_max, y_max = np.max(polygon, axis=0)
    return np.array([x_min, y_min, x_max, y_max])

supervision.detection.utils.converters.xyxy_to_mask(boxes: npt.NDArray[np.number], resolution_wh: tuple[int, int], coordinate_convention: CoordinateConvention = 'inclusive') -> npt.NDArray[np.bool_]

Converts a 2D np.ndarray of bounding boxes into a 3D np.ndarray of bool masks.

Parameters:

Name Type Description Default

boxes

NDArray[number]

A 2D np.ndarray of shape (N, 4) containing bounding boxes (x_min, y_min, x_max, y_max).

required

resolution_wh

tuple[int, int]

A tuple (width, height) specifying the resolution of the output masks.

required

coordinate_convention

CoordinateConvention

How to interpret x_max and y_max. Use "inclusive" for closed boxes with the legacy Supervision convention. Use "exclusive" for half-open boxes that align with box area and IoU calculations.

'inclusive'

Returns:

Type Description
NDArray[bool_]

A 3D np.ndarray of shape (N, height, width) containing 2D bool masks for each bounding box. Inclusive boxes ending left of or above the image produce empty masks, even with fractional coordinates.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> boxes = np.array([[0, 0, 2, 2]])
>>> sv.xyxy_to_mask(boxes, (5, 5))
array([[[ True,  True,  True, False, False],
        [ True,  True,  True, False, False],
        [ True,  True,  True, False, False],
        [False, False, False, False, False],
        [False, False, False, False, False]]])
>>> boxes = np.array([[0, 0, 1, 1], [3, 3, 4, 4]])
>>> sv.xyxy_to_mask(boxes, (5, 5))
array([[[ True,  True, False, False, False],
        [ True,  True, False, False, False],
        [False, False, False, False, False],
        [False, False, False, False, False],
        [False, False, False, False, False]],
<BLANKLINE>
       [[False, False, False, False, False],
        [False, False, False, False, False],
        [False, False, False, False, False],
        [False, False, False,  True,  True],
        [False, False, False,  True,  True]]])
Source code in src/supervision/detection/utils/converters.py
def xyxy_to_mask(
    boxes: npt.NDArray[np.number],
    resolution_wh: tuple[int, int],
    coordinate_convention: CoordinateConvention = "inclusive",
) -> npt.NDArray[np.bool_]:
    """Converts a 2D `np.ndarray` of bounding boxes into a 3D `np.ndarray` of bool
    masks.

    Args:
        boxes: A 2D `np.ndarray` of shape `(N, 4)` containing bounding boxes
            `(x_min, y_min, x_max, y_max)`.
        resolution_wh: A tuple `(width, height)` specifying the resolution of
            the output masks.
        coordinate_convention: How to interpret `x_max` and `y_max`.
            Use `"inclusive"` for closed boxes with the legacy Supervision
            convention. Use `"exclusive"` for half-open boxes that align with
            box area and IoU calculations.

    Returns:
        A 3D `np.ndarray` of shape `(N, height, width)` containing 2D bool masks
            for each bounding box. Inclusive boxes ending left of or above the image
            produce empty masks, even with fractional coordinates.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> boxes = np.array([[0, 0, 2, 2]])
        >>> sv.xyxy_to_mask(boxes, (5, 5))
        array([[[ True,  True,  True, False, False],
                [ True,  True,  True, False, False],
                [ True,  True,  True, False, False],
                [False, False, False, False, False],
                [False, False, False, False, False]]])
        >>> boxes = np.array([[0, 0, 1, 1], [3, 3, 4, 4]])
        >>> sv.xyxy_to_mask(boxes, (5, 5))
        array([[[ True,  True, False, False, False],
                [ True,  True, False, False, False],
                [False, False, False, False, False],
                [False, False, False, False, False],
                [False, False, False, False, False]],
        <BLANKLINE>
               [[False, False, False, False, False],
                [False, False, False, False, False],
                [False, False, False, False, False],
                [False, False, False,  True,  True],
                [False, False, False,  True,  True]]])

        ```
    """
    width, height = resolution_wh
    n = boxes.shape[0]
    masks = np.zeros((n, height, width), dtype=bool)

    for i, (x_min, y_min, x_max, y_max) in enumerate(boxes):
        x_min = max(0, int(x_min))
        y_min = max(0, int(y_min))
        if coordinate_convention == "inclusive":
            # Check before int() truncates negative fractions toward zero.
            if x_max < 0 or y_max < 0:
                continue
            x_max = min(width - 1, int(x_max))
            y_max = min(height - 1, int(y_max))
            if x_max >= x_min and y_max >= y_min:
                masks[i, y_min : y_max + 1, x_min : x_max + 1] = True
        elif coordinate_convention == "exclusive":
            x_max = min(width, int(x_max))
            y_max = min(height, int(y_max))
            if x_max > x_min and y_max > y_min:
                masks[i, y_min:y_max, x_min:x_max] = True
        else:
            raise ValueError(
                "coordinate_convention must be 'inclusive' or 'exclusive', "
                f"got {coordinate_convention!r}."
            )

    return masks

supervision.detection.utils.converters.rle_to_mask(rle: npt.NDArray[np.integer[Any]] | list[int] | str | bytes, resolution_wh: tuple[int, int]) -> npt.NDArray[np.bool_]

Converts a COCO run-length encoding (RLE) to a binary mask.

Implements the COCO RLE format used by pycocotools: pixels are counted in column-major (Fortran) order — top-to-bottom within each column, left-to-right across columns. This is the opposite of the row-major order used by NumPy's default 'C' layout. Passing RLE data produced by a different row-major convention will yield an incorrect mask.

Parameters:

Name Type Description Default

rle

NDArray[integer[Any]] | list[int] | str | bytes

The COCO RLE data in one of the following formats:

  • A 1D array or list of integers (uncompressed COCO RLE, where values at even indices are background run-lengths and values at odd indices are foreground run-lengths, both counted column-major).
  • A compressed COCO RLE string or bytes, as produced by pycocotools.mask.encode.
required

resolution_wh

tuple[int, int]

The width (w) and height (h) of the desired binary mask.

required

Returns:

Type Description
NDArray[bool_]

The generated 2D Boolean mask of shape (h, w), where the foreground object is marked with True's and the rest is filled with False's.

Raises:

Type Description
ValueError

If the sum of pixels encoded in RLE differs from the number of pixels in the expected mask (computed based on resolution_wh).

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> mask = sv.rle_to_mask([5, 2, 2, 2, 5], (4, 4))
>>> mask  # doctest: +NORMALIZE_WHITESPACE
array([[False, False, False, False],
       [False,  True,  True, False],
       [False,  True,  True, False],
       [False, False, False, False]])

>>> mask = sv.rle_to_mask("52203", (4, 4))
>>> mask  # doctest: +NORMALIZE_WHITESPACE
array([[False, False, False, False],
       [False,  True,  True, False],
       [False,  True,  True, False],
       [False, False, False, False]])
Source code in src/supervision/detection/utils/converters.py
def rle_to_mask(
    rle: npt.NDArray[np.integer[Any]] | list[int] | str | bytes,
    resolution_wh: tuple[int, int],
) -> npt.NDArray[np.bool_]:
    """Converts a COCO run-length encoding (RLE) to a binary mask.

    Implements the COCO RLE format used by ``pycocotools``: pixels are counted
    in **column-major (Fortran) order** — top-to-bottom within each column,
    left-to-right across columns. This is the opposite of the row-major order
    used by NumPy's default ``'C'`` layout. Passing RLE data produced by a
    different row-major convention will yield an incorrect mask.

    Args:
        rle: The COCO RLE data in one of the following formats:

            - A 1D array or list of integers (uncompressed COCO RLE, where
              values at even indices are background run-lengths and values at
              odd indices are foreground run-lengths, both counted column-major).
            - A compressed COCO RLE string or bytes, as produced by
              ``pycocotools.mask.encode``.
        resolution_wh: The width (w) and height (h)
            of the desired binary mask.

    Returns:
        The generated 2D Boolean mask of shape `(h, w)`, where the foreground object is
            marked with `True`'s and the rest is filled with `False`'s.

    Raises:
        ValueError: If the sum of pixels encoded in RLE differs from the
            number of pixels in the expected mask (computed based on resolution_wh).

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> mask = sv.rle_to_mask([5, 2, 2, 2, 5], (4, 4))
        >>> mask  # doctest: +NORMALIZE_WHITESPACE
        array([[False, False, False, False],
               [False,  True,  True, False],
               [False,  True,  True, False],
               [False, False, False, False]])

        >>> mask = sv.rle_to_mask("52203", (4, 4))
        >>> mask  # doctest: +NORMALIZE_WHITESPACE
        array([[False, False, False, False],
               [False,  True,  True, False],
               [False,  True,  True, False],
               [False, False, False, False]])

        ```
    """
    if isinstance(rle, bytes):
        rle = rle.decode("utf-8")
    if isinstance(rle, str):
        counts: npt.NDArray[np.int32] = np.array(
            _delta_decode(_base48_decode(rle)), dtype=np.int32
        )
    elif isinstance(rle, list):
        counts = np.array(rle, dtype=np.int32)
    else:
        counts = np.asarray(rle, dtype=np.int32)

    width, height = resolution_wh

    if width * height != np.sum(counts):
        raise ValueError(
            "the sum of the number of pixels in the RLE must be the same "
            "as the number of pixels in the expected mask"
        )

    return _rle_counts_to_mask(counts, height, width)

supervision.detection.utils.converters.mask_to_rle(mask: npt.NDArray[np.bool_], compressed: bool = False) -> list[int] | str

Converts a binary mask into a COCO run-length encoding (RLE).

Produces RLE in the COCO format used by pycocotools: pixels are counted in column-major (Fortran) order — top-to-bottom within each column, left-to-right across columns. The output is directly compatible with pycocotools.mask.decode and COCO annotation JSON files.

Parameters:

Name Type Description Default

mask

NDArray[bool_]

2D binary mask where True indicates foreground object and False indicates background.

required

compressed

bool

If True, return a compressed COCO RLE string compatible with pycocotools. If False (default), return a list of integers.

False

Returns:

Type Description
list[int] | str

The COCO run-length encoded mask. When compressed is False, values of a list with even indices represent the number of pixels assigned as background (False), values of a list with odd indices represent the number of pixels assigned as foreground object (True), both counted in column-major order. When compressed is True, a COCO compressed RLE string.

Raises:

Type Description
ValueError

If input mask is not 2D or is empty.

Examples:

>>> import numpy as np
>>> import supervision as sv
>>> mask = np.array([
...     [True, True, True, True],
...     [True, True, True, True],
...     [True, True, True, True],
...     [True, True, True, True],
... ])
>>> rle = sv.mask_to_rle(mask)
>>> [int(x) for x in rle]
[0, 16]
>>> import numpy as np
>>> import supervision as sv
>>> mask = np.array([
...     [False, False, False, False],
...     [False, True,  True,  False],
...     [False, True,  True,  False],
...     [False, False, False, False],
... ])
>>> rle = sv.mask_to_rle(mask)
>>> [int(x) for x in rle]
[5, 2, 2, 2, 5]

>>> sv.mask_to_rle(mask, compressed=True)
'52203'

mask_to_rle

Source code in src/supervision/detection/utils/converters.py
def mask_to_rle(
    mask: npt.NDArray[np.bool_], compressed: bool = False
) -> list[int] | str:
    """Converts a binary mask into a COCO run-length encoding (RLE).

    Produces RLE in the COCO format used by ``pycocotools``: pixels are counted
    in **column-major (Fortran) order** — top-to-bottom within each column,
    left-to-right across columns. The output is directly compatible with
    ``pycocotools.mask.decode`` and COCO annotation JSON files.

    Args:
        mask: 2D binary mask where `True` indicates foreground
            object and `False` indicates background.
        compressed: If ``True``, return a compressed COCO RLE string
            compatible with ``pycocotools``. If ``False`` (default),
            return a list of integers.

    Returns:
        The COCO run-length encoded mask. When ``compressed`` is ``False``,
            values of a list with even indices represent the number of pixels
            assigned as background (`False`), values of a list with odd indices
            represent the number of pixels assigned as foreground object (`True`),
            both counted in column-major order.
            When ``compressed`` is ``True``, a COCO compressed RLE string.

    Raises:
        ValueError: If input mask is not 2D or is empty.

    Examples:
        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> mask = np.array([
        ...     [True, True, True, True],
        ...     [True, True, True, True],
        ...     [True, True, True, True],
        ...     [True, True, True, True],
        ... ])
        >>> rle = sv.mask_to_rle(mask)
        >>> [int(x) for x in rle]
        [0, 16]

        ```

        ```pycon
        >>> import numpy as np
        >>> import supervision as sv
        >>> mask = np.array([
        ...     [False, False, False, False],
        ...     [False, True,  True,  False],
        ...     [False, True,  True,  False],
        ...     [False, False, False, False],
        ... ])
        >>> rle = sv.mask_to_rle(mask)
        >>> [int(x) for x in rle]
        [5, 2, 2, 2, 5]

        >>> sv.mask_to_rle(mask, compressed=True)
        '52203'

        ```

    ![mask_to_rle](https://media.roboflow.com/supervision-docs/
    mask-to-rle.png){ align=center width="800" }
    """
    if mask.ndim != 2:
        raise ValueError("Input mask must be 2D")
    if mask.size == 0:
        raise ValueError("Input mask cannot be empty")

    counts: list[int] = cast(list[int], _mask_to_rle_counts(mask).tolist())
    if compressed:
        return _base48_encode(_delta_encode(counts))
    return counts

supervision.detection.utils.converters.is_compressed_rle(rle: object) -> bool

Return True if rle is a COCO compressed RLE (str or bytes).

Use this to branch between the compressed-string pipeline (:func:~supervision.detection.utils.converters._base48_decode → :func:~supervision.detection.utils.converters._delta_decode) and the uncompressed integer-list / array pipeline before calling :func:~supervision.detection.utils.converters.rle_to_mask.

Parameters:

Name Type Description Default

rle

object

Candidate RLE value to inspect.

required

Returns:

Type Description
bool

True for str or bytes inputs; False otherwise.

Examples:

>>> from supervision.detection.utils.converters import is_compressed_rle
>>> is_compressed_rle("52203")
True
>>> is_compressed_rle([5, 2, 2, 2, 5])
False
Source code in src/supervision/detection/utils/converters.py
def is_compressed_rle(rle: object) -> bool:
    """Return ``True`` if ``rle`` is a COCO compressed RLE (``str`` or ``bytes``).

    Use this to branch between the compressed-string pipeline
    (:func:`~supervision.detection.utils.converters._base48_decode` →
    :func:`~supervision.detection.utils.converters._delta_decode`) and the
    uncompressed integer-list / array pipeline before calling
    :func:`~supervision.detection.utils.converters.rle_to_mask`.

    Args:
        rle: Candidate RLE value to inspect.

    Returns:
        ``True`` for ``str`` or ``bytes`` inputs; ``False`` otherwise.

    Examples:
        ```pycon
        >>> from supervision.detection.utils.converters import is_compressed_rle
        >>> is_compressed_rle("52203")
        True
        >>> is_compressed_rle([5, 2, 2, 2, 5])
        False

        ```
    """
    return isinstance(rle, (str, bytes))

Comments