Repository: roboflow/supervision
File: supervision/detection/utils.py (Python)

Code before:
    """
    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


def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
    """Generate a mask from a polygon.

    Args:
        polygon (np.ndarray): The polygon for which the mask should be generated,
            given as a list of vertices.
        resolution_wh (Tuple[int, int]): The width and height of the desired resolution.

    Returns:
        np.ndarray: The generated 2D mask, where the polygon is marked with
            `1`'s and the rest is filled with `0`'s.
    """
    width, height = map(int, resolution_wh)
    mask = np.zeros((height, width), dtype=np.uint8)
    cv2.fillPoly(mask, [polygon.astype(np.int32)], color=1)
    return mask


def box_iou(box1: np.ndarray, box2: np.ndarray) -> float:
    """
    Compute the Intersection over Union (IoU) between two bounding boxes.

    Args:
        box1 (np.ndarray): A bounding box represented as [x1, y1, x2, y2].
        box2 (np.ndarray): A bounding box represented as [x1, y1, x2, y2].

    Returns:
        float: The IoU value between box1 and box2.
               Ranges from 0.0 (no overlap) to 1.0 (perfect overlap).
    """
    inter_x1 = max(box1[0], box2[0])
    inter_y1 = max(box1[1], box2[1])
    inter_x2 = min(box1[2], box2[2])
    inter_y2 = min(box1[3], box2[3])

    inter_w = max(0, inter_x2 - inter_x1)
    inter_h = max(0, inter_y2 - inter_y1)

    inter_area = inter_w * inter_h

    area1 = (box1[2] - box1[0]) * (box1[3] - box1[1])
    area2 = (box2[2] - box2[0]) * (box2[3] - box2[1])

    union_area = area1 + area2 - inter_area

Review comment (performance):
- [ ] Let's keep the signature of the function consistent with that of `box_iou_batch`.
- [ ] Let's also ensure that the boxes can be simply represented as lists and well as `np.ndarray`s.


```suggestion
def box_iou(boxes_true: Union[List, np.ndarray], boxes_detection: Union[List, np.ndarray]) -> float:
```

Code after:
    """
    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


def polygon_to_mask(polygon: np.ndarray, resolution_wh: Tuple[int, int]) -> np.ndarray:
    """Generate a mask from a polygon.

    Args:
        polygon (np.ndarray): The polygon for which the mask should be generated,
            given as a list of vertices.
        resolution_wh (Tuple[int, int]): The width and height of the desired resolution.

    Returns:
        np.ndarray: The generated 2D mask, where the polygon is marked with
            `1`'s and the rest is filled with `0`'s.
    """
    width, height = map(int, resolution_wh)
    mask = np.zeros((height, width), dtype=np.uint8)
    cv2.fillPoly(mask, [polygon.astype(np.int32)], color=1)
    return mask


def box_iou(
    box_true: Union[List[float], np.ndarray],
    box_detection: Union[List[float], np.ndarray],
) -> float:
    """
    Compute the Intersection over Union (IoU) between two bounding boxes.

    Both `box_true` and `box_detection` should be in (x_min, y_min, x_max, y_max)
    format.

    Note:
        Use `box_iou` when computing IoU between two individual boxes.
        For comparing multiple boxes (arrays of boxes), use `box_iou_batch` for better
        performance.

    Args:
        box_true (Union[List[float], np.ndarray]): A single bounding box represented as
            [x_min, y_min, x_max, y_max].
        box_detection (Union[List[float], np.ndarray]):
            A single bounding box represented as [x_min, y_min, x_max, y_max].

    Returns:
        IoU (float): IoU score between the two boxes. Ranges from 0.0 (no overlap)
            to 1.0 (perfect overlap).

    Examples: