Skip to content

Module wtracker.utils.bbox_utils

View Source
import numpy as np

from enum import Enum

class BoxFormat(Enum):

    """

    Enumeration representing different box formats.

    Attributes:

        XYWH (int): Represents the box format as (x, y, width, height).

        XYXY (int): Represents the box format as (x1, y1, x2, y2).

        YOLO (int): Represents the box format as (center_x, center_y, width, height).

    """

    XYWH = 0

    XYXY = 1

    YOLO = 2

class BoxUtils:

    """

    A utility class for working with bounding boxes.

    """

    @staticmethod

    def is_bbox(array: np.ndarray) -> bool:

        """

        Check if the given array is a valid bounding box.

        Args:

            array (np.ndarray): The array to check.

        Returns:

            bool: True if the array is a valid bounding box, False otherwise.

        """

        return array.shape[-1] == 4

    @staticmethod

    def unpack(bbox: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:

        """

        Unpack the given bounding box into its individual components.

        Args:

            bbox (np.ndarray): The bounding box to unpack.

        Returns:

            tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: The unpacked components of the bounding box.

        """

        c1, c2, c3, c4 = np.split(bbox, bbox.shape[-1], axis=-1)

        c1 = np.squeeze(c1, axis=-1)

        c2 = np.squeeze(c2, axis=-1)

        c3 = np.squeeze(c3, axis=-1)

        c4 = np.squeeze(c4, axis=-1)

        return c1, c2, c3, c4

    @staticmethod

    def pack(c1: np.ndarray, c2: np.ndarray, c3: np.ndarray, c4: np.ndarray) -> np.ndarray:

        """

        Pack the given components into a single bounding box.

        Args:

            c1 (np.ndarray): The first component of the bounding box.

            c2 (np.ndarray): The second component of the bounding box.

            c3 (np.ndarray): The third component of the bounding box.

            c4 (np.ndarray): The fourth component of the bounding box.

        Returns:

            np.ndarray: The packed bounding box.

        """

        c1 = np.expand_dims(c1, axis=-1)

        c2 = np.expand_dims(c2, axis=-1)

        c3 = np.expand_dims(c3, axis=-1)

        c4 = np.expand_dims(c4, axis=-1)

        return np.concatenate((c1, c2, c3, c4), axis=-1)

    @staticmethod

    def center(bboxes: np.ndarray, box_format: BoxFormat = BoxFormat.XYWH) -> np.ndarray:

        """

        Calculate the center of the bounding boxes.

        Args:

            bboxes (np.ndarray): The input bounding boxes.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The center of the bounding boxes, in the format (center_x, center_y).

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYWH)

        x, y, w, h = BoxUtils.unpack(bboxes)

        center_x = x + w / 2

        center_y = y + h / 2

        return np.array([center_x, center_y]).T

    @staticmethod

    def round(bboxes: np.ndarray, box_format: BoxFormat) -> np.ndarray:

        """

        Rounds the bounding box coordinates to integers.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The bounding box coordinates as integers.

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        x1 = np.floor(x1).astype(np.int32, copy=False)

        y1 = np.floor(y1).astype(np.int32, copy=False)

        x2 = np.ceil(x2).astype(np.int32, copy=False)

        y2 = np.ceil(y2).astype(np.int32, copy=False)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        return BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

    @staticmethod

    def discretize(

        bboxes: np.ndarray,

        bounds: tuple[int, int],

        box_format: BoxFormat,

    ) -> tuple[np.ndarray, np.ndarray]:

        """

        Converts bounding boxes into integer format and clamps them to the specified bounds. All illegal bounding boxes are zeroed out.

        This function is especially useful for discretizing the bboxes for image slicing at bbox coordinates.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            bounds (tuple[int, int]): The bounds to clamp the bounding boxes to, in the format (h, w).

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            tuple[np.ndarray, np.ndarray]: The discretized bounding boxes and a boolean mask indicating which bounding boxes are legal.

                The first element are bounding boxes discretized to 'np.int32' format. All illegal bounding boxes are zeroed out.

                The second element is a boolean mask indicating which input bounding boxes are legal.

        """

        # zero out all non-finite bounding boxes

        is_legal = np.isfinite(bboxes).all(axis=1)

        bboxes[~is_legal] = 0

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        bboxes = BoxUtils.round(bboxes, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        # clip worm bounding boxes to the size

        H, W = bounds

        x1 = np.clip(x1, a_min=0, a_max=W)

        y1 = np.clip(y1, a_min=0, a_max=H)

        x2 = np.clip(x2, a_min=0, a_max=W)

        y2 = np.clip(y2, a_min=0, a_max=H)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        bboxes = BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

        # zero out all bounding boxes with 0 dimension

        w = x2 - x1

        h = y2 - y1

        is_legal = (w > 0.0) & (h > 0.0)

        # zero out all illegal bounding boxes and make sure return types are correct

        bboxes[~is_legal] = 0

        bboxes = bboxes.astype(np.int32, copy=False)

        is_legal = is_legal.astype(bool, copy=False)

        return bboxes, is_legal

class BoxConverter:

    """

    Utility class for converting bounding box coordinates between different formats.

    """

    @staticmethod

    def change_format(bbox: np.ndarray, src_format: BoxFormat, dst_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates from one format to another.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

            dst_format (BoxFormat): The destination format of the bounding box coordinates.

        Returns:

            np.ndarray: The converted bounding box coordinates.

        Raises:

            Exception: If the conversion between the specified formats is not supported.

        """

        if dst_format == BoxFormat.XYXY:

            return BoxConverter.to_xyxy(bbox, src_format)

        elif dst_format == BoxFormat.XYWH:

            return BoxConverter.to_xywh(bbox, src_format)

        elif dst_format == BoxFormat.YOLO:

            return BoxConverter.to_xywh(bbox, src_format)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_xyxy(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYXY format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYXY format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYXY:

            return bbox

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_xywh(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYWH format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYWH format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYWH:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            return BoxUtils.pack(x1, y1, w, h)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            return BoxUtils.pack(x1, y1, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_yolo(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the YOLO format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the YOLO format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.YOLO:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

Classes

BoxConverter

class BoxConverter(
    /,
    *args,
    **kwargs
)

Utility class for converting bounding box coordinates between different formats.

View Source
class BoxConverter:

    """

    Utility class for converting bounding box coordinates between different formats.

    """

    @staticmethod

    def change_format(bbox: np.ndarray, src_format: BoxFormat, dst_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates from one format to another.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

            dst_format (BoxFormat): The destination format of the bounding box coordinates.

        Returns:

            np.ndarray: The converted bounding box coordinates.

        Raises:

            Exception: If the conversion between the specified formats is not supported.

        """

        if dst_format == BoxFormat.XYXY:

            return BoxConverter.to_xyxy(bbox, src_format)

        elif dst_format == BoxFormat.XYWH:

            return BoxConverter.to_xywh(bbox, src_format)

        elif dst_format == BoxFormat.YOLO:

            return BoxConverter.to_xywh(bbox, src_format)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_xyxy(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYXY format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYXY format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYXY:

            return bbox

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_xywh(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYWH format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYWH format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYWH:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            return BoxUtils.pack(x1, y1, w, h)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            return BoxUtils.pack(x1, y1, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

    @staticmethod

    def to_yolo(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the YOLO format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the YOLO format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.YOLO:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

Static methods

change_format

def change_format(
    bbox: numpy.ndarray,
    src_format: wtracker.utils.bbox_utils.BoxFormat,
    dst_format: wtracker.utils.bbox_utils.BoxFormat
) -> numpy.ndarray

Converts the bounding box coordinates from one format to another.

Parameters:

Name Type Description Default
bbox np.ndarray The bounding box coordinates to be converted. None
src_format BoxFormat The source format of the bounding box coordinates. None
dst_format BoxFormat The destination format of the bounding box coordinates. None

Returns:

Type Description
np.ndarray The converted bounding box coordinates.

Raises:

Type Description
Exception If the conversion between the specified formats is not supported.
View Source
    @staticmethod

    def change_format(bbox: np.ndarray, src_format: BoxFormat, dst_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates from one format to another.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

            dst_format (BoxFormat): The destination format of the bounding box coordinates.

        Returns:

            np.ndarray: The converted bounding box coordinates.

        Raises:

            Exception: If the conversion between the specified formats is not supported.

        """

        if dst_format == BoxFormat.XYXY:

            return BoxConverter.to_xyxy(bbox, src_format)

        elif dst_format == BoxFormat.XYWH:

            return BoxConverter.to_xywh(bbox, src_format)

        elif dst_format == BoxFormat.YOLO:

            return BoxConverter.to_xywh(bbox, src_format)

        else:

            raise Exception("unsupported bbox format conversion.")

to_xywh

def to_xywh(
    bbox: numpy.ndarray,
    src_format: wtracker.utils.bbox_utils.BoxFormat
) -> numpy.ndarray

Converts the bounding box coordinates to the XYWH format.

Parameters:

Name Type Description Default
bbox np.ndarray The bounding box coordinates to be converted. None
src_format BoxFormat The source format of the bounding box coordinates. None

Returns:

Type Description
np.ndarray The bounding box coordinates in the XYWH format.

Raises:

Type Description
Exception If the conversion from the specified source format is not supported.
View Source
    @staticmethod

    def to_xywh(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYWH format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYWH format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYWH:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            return BoxUtils.pack(x1, y1, w, h)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            return BoxUtils.pack(x1, y1, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

to_xyxy

def to_xyxy(
    bbox: numpy.ndarray,
    src_format: wtracker.utils.bbox_utils.BoxFormat
) -> numpy.ndarray

Converts the bounding box coordinates to the XYXY format.

Parameters:

Name Type Description Default
bbox np.ndarray The bounding box coordinates to be converted. None
src_format BoxFormat The source format of the bounding box coordinates. None

Returns:

Type Description
np.ndarray The bounding box coordinates in the XYXY format.

Raises:

Type Description
Exception If the conversion from the specified source format is not supported.
View Source
    @staticmethod

    def to_xyxy(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the XYXY format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the XYXY format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.XYXY:

            return bbox

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        elif src_format == BoxFormat.YOLO:

            xm, ym, w, h = BoxUtils.unpack(bbox)

            x1 = xm - w / 2

            y1 = ym - h / 2

            x2 = x1 + w

            y2 = y1 + h

            return BoxUtils.pack(x1, y1, x2, y2)

        else:

            raise Exception("unsupported bbox format conversion.")

to_yolo

def to_yolo(
    bbox: numpy.ndarray,
    src_format: wtracker.utils.bbox_utils.BoxFormat
) -> numpy.ndarray

Converts the bounding box coordinates to the YOLO format.

Parameters:

Name Type Description Default
bbox np.ndarray The bounding box coordinates to be converted. None
src_format BoxFormat The source format of the bounding box coordinates. None

Returns:

Type Description
np.ndarray The bounding box coordinates in the YOLO format.

Raises:

Type Description
Exception If the conversion from the specified source format is not supported.
View Source
    @staticmethod

    def to_yolo(bbox: np.ndarray, src_format: BoxFormat) -> np.ndarray:

        """

        Converts the bounding box coordinates to the YOLO format.

        Args:

            bbox (np.ndarray): The bounding box coordinates to be converted.

            src_format (BoxFormat): The source format of the bounding box coordinates.

        Returns:

            np.ndarray: The bounding box coordinates in the YOLO format.

        Raises:

            Exception: If the conversion from the specified source format is not supported.

        """

        if src_format == BoxFormat.YOLO:

            return bbox

        elif src_format == BoxFormat.XYXY:

            x1, y1, x2, y2 = BoxUtils.unpack(bbox)

            w = x2 - x1

            h = y2 - y1

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        elif src_format == BoxFormat.XYWH:

            x1, y1, w, h = BoxUtils.unpack(bbox)

            xm = x1 + w / 2

            ym = y1 + h / 2

            return BoxUtils.pack(xm, ym, w, h)

        else:

            raise Exception("unsupported bbox format conversion.")

BoxFormat

class BoxFormat(
    /,
    *args,
    **kwargs
)

Enumeration representing different box formats.

Attributes

Name Type Description Default
XYWH int Represents the box format as (x, y, width, height). None
XYXY int Represents the box format as (x1, y1, x2, y2). None
YOLO int Represents the box format as (center_x, center_y, width, height). None
View Source
class BoxFormat(Enum):

    """

    Enumeration representing different box formats.

    Attributes:

        XYWH (int): Represents the box format as (x, y, width, height).

        XYXY (int): Represents the box format as (x1, y1, x2, y2).

        YOLO (int): Represents the box format as (center_x, center_y, width, height).

    """

    XYWH = 0

    XYXY = 1

    YOLO = 2

Ancestors (in MRO)

  • enum.Enum

Class variables

XYWH
XYXY
YOLO
name
value

BoxUtils

class BoxUtils(
    /,
    *args,
    **kwargs
)

A utility class for working with bounding boxes.

View Source
class BoxUtils:

    """

    A utility class for working with bounding boxes.

    """

    @staticmethod

    def is_bbox(array: np.ndarray) -> bool:

        """

        Check if the given array is a valid bounding box.

        Args:

            array (np.ndarray): The array to check.

        Returns:

            bool: True if the array is a valid bounding box, False otherwise.

        """

        return array.shape[-1] == 4

    @staticmethod

    def unpack(bbox: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:

        """

        Unpack the given bounding box into its individual components.

        Args:

            bbox (np.ndarray): The bounding box to unpack.

        Returns:

            tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: The unpacked components of the bounding box.

        """

        c1, c2, c3, c4 = np.split(bbox, bbox.shape[-1], axis=-1)

        c1 = np.squeeze(c1, axis=-1)

        c2 = np.squeeze(c2, axis=-1)

        c3 = np.squeeze(c3, axis=-1)

        c4 = np.squeeze(c4, axis=-1)

        return c1, c2, c3, c4

    @staticmethod

    def pack(c1: np.ndarray, c2: np.ndarray, c3: np.ndarray, c4: np.ndarray) -> np.ndarray:

        """

        Pack the given components into a single bounding box.

        Args:

            c1 (np.ndarray): The first component of the bounding box.

            c2 (np.ndarray): The second component of the bounding box.

            c3 (np.ndarray): The third component of the bounding box.

            c4 (np.ndarray): The fourth component of the bounding box.

        Returns:

            np.ndarray: The packed bounding box.

        """

        c1 = np.expand_dims(c1, axis=-1)

        c2 = np.expand_dims(c2, axis=-1)

        c3 = np.expand_dims(c3, axis=-1)

        c4 = np.expand_dims(c4, axis=-1)

        return np.concatenate((c1, c2, c3, c4), axis=-1)

    @staticmethod

    def center(bboxes: np.ndarray, box_format: BoxFormat = BoxFormat.XYWH) -> np.ndarray:

        """

        Calculate the center of the bounding boxes.

        Args:

            bboxes (np.ndarray): The input bounding boxes.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The center of the bounding boxes, in the format (center_x, center_y).

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYWH)

        x, y, w, h = BoxUtils.unpack(bboxes)

        center_x = x + w / 2

        center_y = y + h / 2

        return np.array([center_x, center_y]).T

    @staticmethod

    def round(bboxes: np.ndarray, box_format: BoxFormat) -> np.ndarray:

        """

        Rounds the bounding box coordinates to integers.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The bounding box coordinates as integers.

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        x1 = np.floor(x1).astype(np.int32, copy=False)

        y1 = np.floor(y1).astype(np.int32, copy=False)

        x2 = np.ceil(x2).astype(np.int32, copy=False)

        y2 = np.ceil(y2).astype(np.int32, copy=False)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        return BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

    @staticmethod

    def discretize(

        bboxes: np.ndarray,

        bounds: tuple[int, int],

        box_format: BoxFormat,

    ) -> tuple[np.ndarray, np.ndarray]:

        """

        Converts bounding boxes into integer format and clamps them to the specified bounds. All illegal bounding boxes are zeroed out.

        This function is especially useful for discretizing the bboxes for image slicing at bbox coordinates.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            bounds (tuple[int, int]): The bounds to clamp the bounding boxes to, in the format (h, w).

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            tuple[np.ndarray, np.ndarray]: The discretized bounding boxes and a boolean mask indicating which bounding boxes are legal.

                The first element are bounding boxes discretized to 'np.int32' format. All illegal bounding boxes are zeroed out.

                The second element is a boolean mask indicating which input bounding boxes are legal.

        """

        # zero out all non-finite bounding boxes

        is_legal = np.isfinite(bboxes).all(axis=1)

        bboxes[~is_legal] = 0

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        bboxes = BoxUtils.round(bboxes, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        # clip worm bounding boxes to the size

        H, W = bounds

        x1 = np.clip(x1, a_min=0, a_max=W)

        y1 = np.clip(y1, a_min=0, a_max=H)

        x2 = np.clip(x2, a_min=0, a_max=W)

        y2 = np.clip(y2, a_min=0, a_max=H)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        bboxes = BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

        # zero out all bounding boxes with 0 dimension

        w = x2 - x1

        h = y2 - y1

        is_legal = (w > 0.0) & (h > 0.0)

        # zero out all illegal bounding boxes and make sure return types are correct

        bboxes[~is_legal] = 0

        bboxes = bboxes.astype(np.int32, copy=False)

        is_legal = is_legal.astype(bool, copy=False)

        return bboxes, is_legal

Static methods

center

def center(
    bboxes: numpy.ndarray,
    box_format: wtracker.utils.bbox_utils.BoxFormat = <BoxFormat.XYWH: 0>
) -> numpy.ndarray

Calculate the center of the bounding boxes.

Parameters:

Name Type Description Default
bboxes np.ndarray The input bounding boxes. None
box_format BoxFormat The format of the input bounding boxes. None

Returns:

Type Description
np.ndarray The center of the bounding boxes, in the format (center_x, center_y).
View Source
    @staticmethod

    def center(bboxes: np.ndarray, box_format: BoxFormat = BoxFormat.XYWH) -> np.ndarray:

        """

        Calculate the center of the bounding boxes.

        Args:

            bboxes (np.ndarray): The input bounding boxes.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The center of the bounding boxes, in the format (center_x, center_y).

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYWH)

        x, y, w, h = BoxUtils.unpack(bboxes)

        center_x = x + w / 2

        center_y = y + h / 2

        return np.array([center_x, center_y]).T

discretize

def discretize(
    bboxes: numpy.ndarray,
    bounds: tuple[int, int],
    box_format: wtracker.utils.bbox_utils.BoxFormat
) -> tuple[numpy.ndarray, numpy.ndarray]

Converts bounding boxes into integer format and clamps them to the specified bounds. All illegal bounding boxes are zeroed out.

This function is especially useful for discretizing the bboxes for image slicing at bbox coordinates.

Parameters:

Name Type Description Default
bboxes np.ndarray The bounding box coordinates to convert. None
bounds tuple[int, int] The bounds to clamp the bounding boxes to, in the format (h, w). None
box_format BoxFormat The format of the input bounding boxes. None

Returns:

Type Description
tuple[np.ndarray, np.ndarray] The discretized bounding boxes and a boolean mask indicating which bounding boxes are legal.
The first element are bounding boxes discretized to 'np.int32' format. All illegal bounding boxes are zeroed out.
The second element is a boolean mask indicating which input bounding boxes are legal.
View Source
    @staticmethod

    def discretize(

        bboxes: np.ndarray,

        bounds: tuple[int, int],

        box_format: BoxFormat,

    ) -> tuple[np.ndarray, np.ndarray]:

        """

        Converts bounding boxes into integer format and clamps them to the specified bounds. All illegal bounding boxes are zeroed out.

        This function is especially useful for discretizing the bboxes for image slicing at bbox coordinates.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            bounds (tuple[int, int]): The bounds to clamp the bounding boxes to, in the format (h, w).

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            tuple[np.ndarray, np.ndarray]: The discretized bounding boxes and a boolean mask indicating which bounding boxes are legal.

                The first element are bounding boxes discretized to 'np.int32' format. All illegal bounding boxes are zeroed out.

                The second element is a boolean mask indicating which input bounding boxes are legal.

        """

        # zero out all non-finite bounding boxes

        is_legal = np.isfinite(bboxes).all(axis=1)

        bboxes[~is_legal] = 0

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        bboxes = BoxUtils.round(bboxes, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        # clip worm bounding boxes to the size

        H, W = bounds

        x1 = np.clip(x1, a_min=0, a_max=W)

        y1 = np.clip(y1, a_min=0, a_max=H)

        x2 = np.clip(x2, a_min=0, a_max=W)

        y2 = np.clip(y2, a_min=0, a_max=H)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        bboxes = BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

        # zero out all bounding boxes with 0 dimension

        w = x2 - x1

        h = y2 - y1

        is_legal = (w > 0.0) & (h > 0.0)

        # zero out all illegal bounding boxes and make sure return types are correct

        bboxes[~is_legal] = 0

        bboxes = bboxes.astype(np.int32, copy=False)

        is_legal = is_legal.astype(bool, copy=False)

        return bboxes, is_legal

is_bbox

def is_bbox(
    array: numpy.ndarray
) -> bool

Check if the given array is a valid bounding box.

Parameters:

Name Type Description Default
array np.ndarray The array to check. None

Returns:

Type Description
bool True if the array is a valid bounding box, False otherwise.
View Source
    @staticmethod

    def is_bbox(array: np.ndarray) -> bool:

        """

        Check if the given array is a valid bounding box.

        Args:

            array (np.ndarray): The array to check.

        Returns:

            bool: True if the array is a valid bounding box, False otherwise.

        """

        return array.shape[-1] == 4

pack

def pack(
    c1: numpy.ndarray,
    c2: numpy.ndarray,
    c3: numpy.ndarray,
    c4: numpy.ndarray
) -> numpy.ndarray

Pack the given components into a single bounding box.

Parameters:

Name Type Description Default
c1 np.ndarray The first component of the bounding box. None
c2 np.ndarray The second component of the bounding box. None
c3 np.ndarray The third component of the bounding box. None
c4 np.ndarray The fourth component of the bounding box. None

Returns:

Type Description
np.ndarray The packed bounding box.
View Source
    @staticmethod

    def pack(c1: np.ndarray, c2: np.ndarray, c3: np.ndarray, c4: np.ndarray) -> np.ndarray:

        """

        Pack the given components into a single bounding box.

        Args:

            c1 (np.ndarray): The first component of the bounding box.

            c2 (np.ndarray): The second component of the bounding box.

            c3 (np.ndarray): The third component of the bounding box.

            c4 (np.ndarray): The fourth component of the bounding box.

        Returns:

            np.ndarray: The packed bounding box.

        """

        c1 = np.expand_dims(c1, axis=-1)

        c2 = np.expand_dims(c2, axis=-1)

        c3 = np.expand_dims(c3, axis=-1)

        c4 = np.expand_dims(c4, axis=-1)

        return np.concatenate((c1, c2, c3, c4), axis=-1)

round

def round(
    bboxes: numpy.ndarray,
    box_format: wtracker.utils.bbox_utils.BoxFormat
) -> numpy.ndarray

Rounds the bounding box coordinates to integers.

Parameters:

Name Type Description Default
bboxes np.ndarray The bounding box coordinates to convert. None
box_format BoxFormat The format of the input bounding boxes. None

Returns:

Type Description
np.ndarray The bounding box coordinates as integers.
View Source
    @staticmethod

    def round(bboxes: np.ndarray, box_format: BoxFormat) -> np.ndarray:

        """

        Rounds the bounding box coordinates to integers.

        Args:

            bboxes (np.ndarray): The bounding box coordinates to convert.

            box_format (BoxFormat): The format of the input bounding boxes.

        Returns:

            np.ndarray: The bounding box coordinates as integers.

        """

        bboxes = BoxConverter.change_format(bboxes, box_format, BoxFormat.XYXY)

        x1, y1, x2, y2 = BoxUtils.unpack(bboxes)

        x1 = np.floor(x1).astype(np.int32, copy=False)

        y1 = np.floor(y1).astype(np.int32, copy=False)

        x2 = np.ceil(x2).astype(np.int32, copy=False)

        y2 = np.ceil(y2).astype(np.int32, copy=False)

        bboxes = BoxUtils.pack(x1, y1, x2, y2)

        return BoxConverter.change_format(bboxes, BoxFormat.XYXY, box_format)

unpack

def unpack(
    bbox: numpy.ndarray
) -> tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray]

Unpack the given bounding box into its individual components.

Parameters:

Name Type Description Default
bbox np.ndarray The bounding box to unpack. None

Returns:

Type Description
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray] The unpacked components of the bounding box.
View Source
    @staticmethod

    def unpack(bbox: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:

        """

        Unpack the given bounding box into its individual components.

        Args:

            bbox (np.ndarray): The bounding box to unpack.

        Returns:

            tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: The unpacked components of the bounding box.

        """

        c1, c2, c3, c4 = np.split(bbox, bbox.shape[-1], axis=-1)

        c1 = np.squeeze(c1, axis=-1)

        c2 = np.squeeze(c2, axis=-1)

        c3 = np.squeeze(c3, axis=-1)

        c4 = np.squeeze(c4, axis=-1)

        return c1, c2, c3, c4