Skip to content

API Reference

Rectangle Drawing

draw_box module-attribute

draw_box = draw_rectangle

draw_multiple_boxes module-attribute

draw_multiple_boxes = draw_multiple_rectangles

draw_rectangle

draw_rectangle(img: NDArray[uint8], bbox: Sequence[float], bbox_color: tuple[int, int, int] = (255, 255, 255), thickness: int = 3, is_opaque: bool = False, alpha: float = 0.5, bbox_format: str = 'voc') -> NDArray[np.uint8]

Draws a rectangle around an object in the image.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
bbox Sequence[float]

Bounding box coordinates in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
bbox_color tuple[int, int, int]

BGR color tuple for the box (default: white)

(255, 255, 255)
thickness int

Line thickness in pixels (default: 3)

3
is_opaque bool

If True, draws filled rectangle with transparency (default: False)

False
alpha float

Transparency level for filled rectangles (default: 0.5)

0.5
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with drawn rectangle; the input image is not modified

Source code in bbox_visualizer/core/rectangle.py
def draw_rectangle(
    img: NDArray[np.uint8],
    bbox: Sequence[float],
    bbox_color: tuple[int, int, int] = (255, 255, 255),
    thickness: int = 3,
    is_opaque: bool = False,
    alpha: float = 0.5,
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Draws a rectangle around an object in the image.

    Args:
        img: Input image array
        bbox: Bounding box coordinates in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        bbox_color: BGR color tuple for the box (default: white)
        thickness: Line thickness in pixels (default: 3)
        is_opaque: If True, draws filled rectangle with transparency (default: False)
        alpha: Transparency level for filled rectangles (default: 0.5)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with drawn rectangle; the input image is not modified

    """
    bbox_color = _validate_color(bbox_color)
    bbox = _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)

    output = img.copy()
    if not is_opaque:
        # Shift the stroke inward so its outer edge lies on the bbox coordinates
        # (cv2 centers the stroke on the coords, spilling outside the box and
        # misaligning with labels drawn flush at bbox[0]). cv2's measured stroke
        # half-width is (t+1)//2 for t > 1, not t//2.
        shift = (thickness + 1) // 2 if thickness > 1 else 0
        cv2.rectangle(
            output,
            (bbox[0] + shift, bbox[1] + shift),
            (bbox[2] - shift, bbox[3] - shift),
            bbox_color,
            thickness,
        )
    else:
        overlay = img.copy()
        cv2.rectangle(overlay, (bbox[0], bbox[1]), (bbox[2], bbox[3]), bbox_color, -1)
        cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)

    return output

draw_multiple_rectangles

draw_multiple_rectangles(img: NDArray[uint8], bboxes: Sequence[Sequence[float]], bbox_color: tuple[int, int, int] | Sequence[tuple[int, int, int]] = (255, 255, 255), thickness: int = 3, is_opaque: bool = False, alpha: float = 0.5, bbox_format: str = 'voc') -> NDArray[np.uint8]

Draws multiple rectangles on the image using optimized batched operations.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
bboxes Sequence[Sequence[float]]

List of bounding boxes, each in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
bbox_color tuple[int, int, int] | Sequence[tuple[int, int, int]]

BGR color tuple applied to all boxes, or a sequence of one color per box (default: white)

(255, 255, 255)
thickness int

Line thickness in pixels (default: 3)

3
is_opaque bool

If True, draws filled rectangles with transparency (default: False)

False
alpha float

Transparency level for filled rectangles (default: 0.5)

0.5
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with all rectangles drawn; the input image is not modified

Source code in bbox_visualizer/core/rectangle.py
def draw_multiple_rectangles(
    img: NDArray[np.uint8],
    bboxes: Sequence[Sequence[float]],
    bbox_color: tuple[int, int, int] | Sequence[tuple[int, int, int]] = (
        255,
        255,
        255,
    ),
    thickness: int = 3,
    is_opaque: bool = False,
    alpha: float = 0.5,
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Draws multiple rectangles on the image using optimized batched operations.

    Args:
        img: Input image array
        bboxes: List of bounding boxes, each in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        bbox_color: BGR color tuple applied to all boxes, or a sequence of
            one color per box (default: white)
        thickness: Line thickness in pixels (default: 3)
        is_opaque: If True, draws filled rectangles with transparency (default: False)
        alpha: Transparency level for filled rectangles (default: 0.5)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with all rectangles drawn; the input image is not modified

    """
    # len() instead of truthiness: numpy arrays raise on ambiguous bool()
    if bboxes is None or len(bboxes) == 0:
        raise ValueError("List of bounding boxes cannot be empty")

    per_box_colors = len(bbox_color) > 0 and isinstance(bbox_color[0], tuple | list)
    colors: list[tuple[int, int, int]]
    if per_box_colors:
        if len(bbox_color) != len(bboxes):
            raise ValueError(
                f"Number of colors ({len(bbox_color)}) must match "
                f"number of bounding boxes ({len(bboxes)})"
            )
        color_seq = cast("Sequence[tuple[int, int, int]]", bbox_color)
        colors = [tuple(color) for color in color_seq]
    else:
        colors = [cast("tuple[int, int, int]", bbox_color)] * len(bboxes)
    colors = [_validate_color(color) for color in colors]

    # Validate and modify all bboxes
    validated_bboxes = [
        _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)
        for bbox in bboxes
    ]

    output = img.copy()

    if not is_opaque:
        # Shift the stroke inward so its outer edge lies on the bbox coordinates,
        # matching draw_rectangle
        shift = (thickness + 1) // 2 if thickness > 1 else 0
        if per_box_colors:
            # cv2.polylines batches only a single color, so draw box by box
            for bbox, color in zip(validated_bboxes, colors, strict=True):
                cv2.rectangle(
                    output,
                    (bbox[0] + shift, bbox[1] + shift),
                    (bbox[2] - shift, bbox[3] - shift),
                    color,
                    thickness,
                )
        else:
            # Convert bboxes to contours for cv2.polylines
            # (draws all rectangles in one call)
            contours = [
                np.array(
                    [
                        [bbox[0] + shift, bbox[1] + shift],
                        [bbox[2] - shift, bbox[1] + shift],
                        [bbox[2] - shift, bbox[3] - shift],
                        [bbox[0] + shift, bbox[3] - shift],
                    ],
                    dtype=np.int32,
                )
                for bbox in validated_bboxes
            ]
            cv2.polylines(
                output, contours, isClosed=True, color=colors[0], thickness=thickness
            )
    else:
        # For opaque rectangles: draw all filled rectangles on one overlay,
        # then do a single alpha blend
        overlay = img.copy()
        for bbox, color in zip(validated_bboxes, colors, strict=True):
            cv2.rectangle(overlay, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, -1)
        cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0, output)

    return output

Label Drawing

add_label

add_label(img: NDArray[uint8], label: str, bbox: Sequence[float], size: float = 1, thickness: int = 2, draw_bg: bool = True, text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), top: bool = True, bbox_format: str = 'voc') -> NDArray[np.uint8]

Add a label to a bounding box, either above or inside it.

If there isn't enough space above the box, the label is placed inside. The label has an optional background rectangle for better visibility.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
label str

Text to display

required
bbox Sequence[float]

Bounding box coordinates in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
size float

Font size multiplier (default: 1)

1
thickness int

Text thickness in pixels (default: 2)

2
draw_bg bool

Whether to draw background rectangle (default: True)

True
text_bg_color tuple[int, int, int]

BGR color tuple for text background (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
top bool

If True, place label above box; if False, inside (default: True)

True
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with added label; the input image is not modified

Source code in bbox_visualizer/core/labels.py
def add_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: Sequence[float],
    size: float = 1,
    thickness: int = 2,
    draw_bg: bool = True,
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    top: bool = True,
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Add a label to a bounding box, either above or inside it.

    If there isn't enough space above the box, the label is placed inside.
    The label has an optional background rectangle for better visibility.

    Args:
        img: Input image array
        label: Text to display
        bbox: Bounding box coordinates in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        size: Font size multiplier (default: 1)
        thickness: Text thickness in pixels (default: 2)
        draw_bg: Whether to draw background rectangle (default: True)
        text_bg_color: BGR color tuple for text background (default: white)
        text_color: BGR color tuple for text (default: black)
        top: If True, place label above box; if False, inside (default: True)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with added label; the input image is not modified

    """
    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)
    bbox = _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)
    img = img.copy()

    (text_width, text_height), baseline = _get_text_size(label, size, thickness)
    padding = 5  # Padding around text

    bg_width = text_width + 2 * padding
    # Include the font baseline so descenders (p, q, g, ...) stay inside the bg
    bg_height = text_height + baseline + 2 * padding

    # Compare against the full background height so the label only goes above
    # the box when the whole background fits inside the image
    label_above = top and bbox[1] >= bg_height
    bg_x1 = bbox[0]
    bg_y1 = bbox[1] - bg_height if label_above else bbox[1]
    bg_x2 = bg_x1 + bg_width
    bg_y2 = bg_y1 + bg_height

    if draw_bg:
        cv2.rectangle(
            img,
            (bg_x1, bg_y1),
            (bg_x2, bg_y2),
            text_bg_color,
            -1,
        )

    text_x = bg_x1 + padding
    text_y = bg_y1 + padding + text_height  # text baseline; descenders fit below

    cv2.putText(
        img,
        label,
        (text_x, text_y),
        font,
        size,
        text_color,
        thickness,
    )
    return img

add_multiple_labels

add_multiple_labels(img: NDArray[uint8], labels: list[str], bboxes: Sequence[Sequence[float]], size: float = 1, thickness: int = 2, draw_bg: bool = True, text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), top: bool = True, bbox_format: str = 'voc') -> NDArray[np.uint8]

Add multiple labels to their corresponding bounding boxes using optimized operations.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
labels list[str]

List of text labels

required
bboxes Sequence[Sequence[float]]

List of bounding boxes, each in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
size float

Font size multiplier (default: 1)

1
thickness int

Text thickness in pixels (default: 2)

2
draw_bg bool

Whether to draw background rectangles (default: True)

True
text_bg_color tuple[int, int, int]

BGR color tuple for text backgrounds (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
top bool

If True, place labels above boxes; if False, inside (default: True)

True
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with all labels added; the input image is not modified

Source code in bbox_visualizer/core/labels.py
def add_multiple_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: Sequence[Sequence[float]],
    size: float = 1,
    thickness: int = 2,
    draw_bg: bool = True,
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    top: bool = True,
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Add multiple labels to their corresponding bounding boxes using optimized operations.

    Args:
        img: Input image array
        labels: List of text labels
        bboxes: List of bounding boxes, each in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        size: Font size multiplier (default: 1)
        thickness: Text thickness in pixels (default: 2)
        draw_bg: Whether to draw background rectangles (default: True)
        text_bg_color: BGR color tuple for text backgrounds (default: white)
        text_color: BGR color tuple for text (default: black)
        top: If True, place labels above boxes; if False, inside (default: True)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with all labels added; the input image is not modified

    """
    # len() instead of truthiness: numpy arrays raise on ambiguous bool()
    if bboxes is None or labels is None or len(bboxes) == 0 or len(labels) == 0:
        raise ValueError("Lists of bounding boxes and labels cannot be empty")
    if len(bboxes) != len(labels):
        raise ValueError("Number of bounding boxes must match number of labels")

    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)

    # Validate and convert all bboxes to VOC format up front
    converted_bboxes = [
        _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)
        for bbox in bboxes
    ]

    # Draw all labels using add_label (which copies, keeping this function pure)
    output = img
    for label, bbox in zip(labels, converted_bboxes, strict=True):
        output = add_label(
            output,
            label,
            bbox,
            size,
            thickness,
            draw_bg,
            text_bg_color,
            text_color,
            top,
        )
    return output

Special Labels

add_T_label

add_T_label(img: NDArray[uint8], label: str, bbox: Sequence[float], size: float = 1, thickness: int = 2, draw_bg: bool = True, text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), bbox_format: str = 'voc') -> NDArray[np.uint8]

Add a T-shaped label with a vertical line connecting to the bounding box.

The label consists of a vertical line extending from the top of the box and a horizontal label at the top. Falls back to regular label if there isn't enough space above the box.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
label str

Text to display

required
bbox Sequence[float]

Bounding box coordinates in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
size float

Font size multiplier (default: 1)

1
thickness int

Text thickness in pixels (default: 2)

2
draw_bg bool

Whether to draw background rectangle (default: True)

True
text_bg_color tuple[int, int, int]

BGR color tuple for text background (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with added T-shaped label; the input image is not modified

Source code in bbox_visualizer/core/flags.py
def add_T_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: Sequence[float],
    size: float = 1,
    thickness: int = 2,
    draw_bg: bool = True,
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Add a T-shaped label with a vertical line connecting to the bounding box.

    The label consists of a vertical line extending from the top of the box
    and a horizontal label at the top. Falls back to regular label if there isn't
    enough space above the box.

    Args:
        img: Input image array
        label: Text to display
        bbox: Bounding box coordinates in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        size: Font size multiplier (default: 1)
        thickness: Text thickness in pixels (default: 2)
        draw_bg: Whether to draw background rectangle (default: True)
        text_bg_color: BGR color tuple for text background (default: white)
        text_color: BGR color tuple for text (default: black)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with added T-shaped label; the input image is not modified

    """
    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)
    bbox = _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)
    img = img.copy()
    (label_width, label_height), baseline = _get_text_size(label, size, thickness)
    padding = 5  # Padding around text

    # draw vertical line
    x_center = (bbox[0] + bbox[2]) // 2
    line_top = y_top = bbox[1] - T_LINE_LENGTH

    # draw rectangle with label
    y_bottom = y_top
    y_top = y_bottom - (label_height + baseline + 2 * padding)

    if y_top < 0:
        logger.warning(
            "Labelling style 'T' going out of frame. Falling back to normal labeling."
        )
        return add_label(
            img,
            label,
            bbox,
            size=size,
            thickness=thickness,
            draw_bg=draw_bg,
            text_bg_color=text_bg_color,
            text_color=text_color,
        )

    cv2.line(img, (x_center, bbox[1]), (x_center, line_top), text_bg_color, 3)

    # Calculate background rectangle dimensions
    bg_width = label_width + 2 * padding
    # Include the font baseline so descenders (p, q, g, ...) stay inside the bg
    bg_height = label_height + baseline + 2 * padding

    # Calculate background rectangle position
    bg_x1 = x_center - (bg_width // 2)
    bg_y1 = y_top
    bg_x2 = bg_x1 + bg_width
    bg_y2 = bg_y1 + bg_height

    if draw_bg:
        cv2.rectangle(img, (bg_x1, bg_y1), (bg_x2, bg_y2), text_bg_color, -1)

    text_x = bg_x1 + padding
    text_y = bg_y1 + padding + label_height  # text baseline; descenders fit below

    cv2.putText(
        img,
        label,
        (text_x, text_y),
        font,
        size,
        text_color,
        thickness,
    )

    return img

add_multiple_T_labels

add_multiple_T_labels(img: NDArray[uint8], labels: list[str], bboxes: Sequence[Sequence[float]], draw_bg: bool = True, text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), bbox_format: str = 'voc') -> NDArray[np.uint8]

Add multiple T-shaped labels to their corresponding bounding boxes.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
labels list[str]

List of text labels

required
bboxes Sequence[Sequence[float]]

List of bounding boxes, each in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
draw_bg bool

Whether to draw background rectangles (default: True)

True
text_bg_color tuple[int, int, int]

BGR color tuple for text backgrounds (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with all T-shaped labels added; the input image is not modified

Source code in bbox_visualizer/core/flags.py
def add_multiple_T_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: Sequence[Sequence[float]],
    draw_bg: bool = True,
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Add multiple T-shaped labels to their corresponding bounding boxes.

    Args:
        img: Input image array
        labels: List of text labels
        bboxes: List of bounding boxes, each in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        draw_bg: Whether to draw background rectangles (default: True)
        text_bg_color: BGR color tuple for text backgrounds (default: white)
        text_color: BGR color tuple for text (default: black)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with all T-shaped labels added; the input image is not modified

    """
    # len() instead of truthiness: numpy arrays raise on ambiguous bool()
    if bboxes is None or labels is None or len(bboxes) == 0 or len(labels) == 0:
        raise ValueError("Lists of bounding boxes and labels cannot be empty")
    if len(bboxes) != len(labels):
        raise ValueError("Number of bounding boxes must match number of labels")

    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)
    for label, bbox in zip(labels, bboxes, strict=True):
        img = add_T_label(
            img,
            label,
            bbox,
            size=1,
            thickness=2,
            draw_bg=draw_bg,
            text_bg_color=text_bg_color,
            text_color=text_color,
            bbox_format=bbox_format,
        )

    return img

draw_flag_with_label

draw_flag_with_label(img: NDArray[uint8], label: str, bbox: Sequence[float], size: float = 1, thickness: int = 2, write_label: bool = True, line_color: tuple[int, int, int] = (255, 255, 255), text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), bbox_format: str = 'voc') -> NDArray[np.uint8]

Draws a flag-like label with a vertical line and text box.

The flag consists of a vertical line extending from the middle of the box and a horizontal label at the top. Falls back to regular label if there isn't enough space above the box.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
label str

Text to display

required
bbox Sequence[float]

Bounding box coordinates in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
size float

Font size multiplier (default: 1)

1
thickness int

Text thickness in pixels (default: 2)

2
write_label bool

Whether to draw the text label (default: True)

True
line_color tuple[int, int, int]

BGR color tuple for the vertical line (default: white)

(255, 255, 255)
text_bg_color tuple[int, int, int]

BGR color tuple for text background (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with added flag label; the input image is not modified

Source code in bbox_visualizer/core/flags.py
def draw_flag_with_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: Sequence[float],
    size: float = 1,
    thickness: int = 2,
    write_label: bool = True,
    line_color: tuple[int, int, int] = (255, 255, 255),
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Draws a flag-like label with a vertical line and text box.

    The flag consists of a vertical line extending from the middle of the box
    and a horizontal label at the top. Falls back to regular label if there isn't
    enough space above the box.

    Args:
        img: Input image array
        label: Text to display
        bbox: Bounding box coordinates in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        size: Font size multiplier (default: 1)
        thickness: Text thickness in pixels (default: 2)
        write_label: Whether to draw the text label (default: True)
        line_color: BGR color tuple for the vertical line (default: white)
        text_bg_color: BGR color tuple for text background (default: white)
        text_color: BGR color tuple for text (default: black)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with added flag label; the input image is not modified

    """
    line_color = _validate_color(line_color)
    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)
    bbox = _check_and_modify_bbox(bbox, img.shape, bbox_format=bbox_format)
    img = img.copy()
    (label_width, label_height), baseline = _get_text_size(label, size, thickness)

    x_center = (bbox[0] + bbox[2]) // 2
    y_bottom = int(bbox[1] * 0.75 + bbox[3] * 0.25)
    y_top = bbox[1] - (y_bottom - bbox[1])
    if y_top < 0:
        logger.warning(
            "Labelling style 'Flag' going out of frame. Falling back to normal labeling."
        )
        img = draw_rectangle(img, bbox, bbox_color=line_color)
        return add_label(
            img,
            label,
            bbox,
            size=size,
            thickness=thickness,
            text_bg_color=text_bg_color,
            text_color=text_color,
        )

    start_point = (x_center, y_top)
    end_point = (x_center, y_bottom)

    cv2.line(img, start_point, end_point, line_color, 3)

    # write label
    if write_label:
        padding = 5  # Padding around text
        bg_x2 = start_point[0] + label_width + 2 * padding
        # Include the font baseline so descenders (p, q, g, ...) stay inside the bg
        bg_y2 = start_point[1] + label_height + baseline + 2 * padding
        cv2.rectangle(img, start_point, (bg_x2, bg_y2), text_bg_color, -1)
        cv2.putText(
            img,
            label,
            (start_point[0] + padding, start_point[1] + padding + label_height),
            font,
            size,
            text_color,
            thickness,
        )

    return img

draw_multiple_flags_with_labels

draw_multiple_flags_with_labels(img: NDArray[uint8], labels: list[str], bboxes: Sequence[Sequence[float]], write_label: bool = True, line_color: tuple[int, int, int] = (255, 255, 255), text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0), bbox_format: str = 'voc') -> NDArray[np.uint8]

Add multiple flag-like labels to their corresponding bounding boxes.

Parameters:

Name Type Description Default
img NDArray[uint8]

Input image array

required
labels list[str]

List of text labels

required
bboxes Sequence[Sequence[float]]

List of bounding boxes, each in bbox_format (default VOC: [x_min, y_min, x_max, y_max])

required
write_label bool

Whether to draw the text labels (default: True)

True
line_color tuple[int, int, int]

BGR color tuple for the vertical lines (default: white)

(255, 255, 255)
text_bg_color tuple[int, int, int]

BGR color tuple for text backgrounds (default: white)

(255, 255, 255)
text_color tuple[int, int, int]

BGR color tuple for text (default: black)

(0, 0, 0)
bbox_format str

Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

'voc'

Returns:

Type Description
NDArray[uint8]

New image with all flag labels added; the input image is not modified

Source code in bbox_visualizer/core/flags.py
def draw_multiple_flags_with_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: Sequence[Sequence[float]],
    write_label: bool = True,
    line_color: tuple[int, int, int] = (255, 255, 255),
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
    bbox_format: str = "voc",
) -> NDArray[np.uint8]:
    """Add multiple flag-like labels to their corresponding bounding boxes.

    Args:
        img: Input image array
        labels: List of text labels
        bboxes: List of bounding boxes, each in ``bbox_format`` (default VOC:
            [x_min, y_min, x_max, y_max])
        write_label: Whether to draw the text labels (default: True)
        line_color: BGR color tuple for the vertical lines (default: white)
        text_bg_color: BGR color tuple for text backgrounds (default: white)
        text_color: BGR color tuple for text (default: black)
        bbox_format: Input bbox format, one of "voc", "coco", "yolo" (default: "voc")

    Returns:
        New image with all flag labels added; the input image is not modified

    """
    # len() instead of truthiness: numpy arrays raise on ambiguous bool()
    if bboxes is None or labels is None or len(bboxes) == 0 or len(labels) == 0:
        raise ValueError("Lists of bounding boxes and labels cannot be empty")
    if len(bboxes) != len(labels):
        raise ValueError("Number of bounding boxes must match number of labels")

    line_color = _validate_color(line_color)
    text_bg_color = _validate_color(text_bg_color)
    text_color = _validate_color(text_color)
    for label, bbox in zip(labels, bboxes, strict=True):
        img = draw_flag_with_label(
            img,
            label,
            bbox,
            size=1,
            thickness=2,
            write_label=write_label,
            line_color=line_color,
            text_bg_color=text_bg_color,
            text_color=text_color,
            bbox_format=bbox_format,
        )

    return img