Skip to content

API Reference

Warning Control

suppress_warnings

suppress_warnings(suppress: bool = True) -> None

Suppress or enable warning messages from bbox-visualizer.

Parameters:

Name Type Description Default
suppress bool

If True, suppress all warnings. If False, enable warnings.

True
Source code in bbox_visualizer/core/_utils.py
def suppress_warnings(suppress: bool = True) -> None:
    """Suppress or enable warning messages from bbox-visualizer.

    Args:
        suppress: If True, suppress all warnings. If False, enable warnings.

    """
    global _warnings_suppressed
    _warnings_suppressed = suppress

warnings_suppressed

warnings_suppressed() -> Generator[None, None, None]

Temporarily suppress warnings.

Example
with warnings_suppressed():
    # Warnings will be suppressed in this block
    image = bbv.draw_flag_with_label(image, "Object", bbox)
Source code in bbox_visualizer/core/_utils.py
@contextmanager
def warnings_suppressed() -> Generator[None, None, None]:
    """Temporarily suppress warnings.

    Example:
        ```python
        with warnings_suppressed():
            # Warnings will be suppressed in this block
            image = bbv.draw_flag_with_label(image, "Object", bbox)
        ```
    """
    previous_state = _warnings_suppressed
    suppress_warnings(True)
    try:
        yield
    finally:
        suppress_warnings(previous_state)

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: list[int], bbox_color: tuple[int, int, int] = (255, 255, 255), thickness: int = 3, is_opaque: bool = False, alpha: float = 0.5) -> 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 list[int]

List of [x_min, y_min, x_max, y_max] coordinates

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

Returns:

Type Description
NDArray[uint8]

Image with drawn rectangle

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

    Args:
        img: Input image array
        bbox: List of [x_min, y_min, x_max, y_max] coordinates
        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)

    Returns:
        Image with drawn rectangle

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

    output = img.copy()
    if not is_opaque:
        cv2.rectangle(
            output, (bbox[0], bbox[1]), (bbox[2], bbox[3]), 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: list[list[int]], bbox_color: tuple[int, int, int] = (255, 255, 255), thickness: int = 3, is_opaque: bool = False, alpha: float = 0.5) -> 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 list[list[int]]

List of bounding boxes, each containing [x_min, y_min, x_max, y_max]

required
bbox_color tuple[int, int, int]

BGR color tuple for the boxes (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

Returns:

Type Description
NDArray[uint8]

Image with all rectangles drawn

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

    Args:
        img: Input image array
        bboxes: List of bounding boxes, each containing [x_min, y_min, x_max, y_max]
        bbox_color: BGR color tuple for the boxes (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)

    Returns:
        Image with all rectangles drawn

    """
    if not bboxes:
        raise ValueError("List of bounding boxes cannot be empty")
    _validate_color(bbox_color)

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

    output = img.copy()

    if not is_opaque:
        # Convert bboxes to contours for cv2.polylines (draws all rectangles in one call)
        contours = [
            np.array(
                [
                    [bbox[0], bbox[1]],
                    [bbox[2], bbox[1]],
                    [bbox[2], bbox[3]],
                    [bbox[0], bbox[3]],
                ],
                dtype=np.int32,
            )
            for bbox in validated_bboxes
        ]
        cv2.polylines(output, contours, isClosed=True, color=bbox_color, thickness=thickness)
    else:
        # For opaque rectangles: draw all filled rectangles on one overlay,
        # then do a single alpha blend
        overlay = img.copy()
        for bbox in validated_bboxes:
            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

Label Drawing

add_label

add_label(img: NDArray[uint8], label: str, bbox: list[int], 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) -> 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 list[int]

List of [x_min, y_min, x_max, y_max] coordinates

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

Returns:

Type Description
NDArray[uint8]

Image with added label

Source code in bbox_visualizer/core/labels.py
def add_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: list[int],
    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,
) -> 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: List of [x_min, y_min, x_max, y_max] coordinates
        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)

    Returns:
        Image with added label

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

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

    if top and bbox[1] - text_height > text_height:
        # Calculate background rectangle dimensions
        bg_width = text_width + 2 * padding
        bg_height = text_height + 2 * padding

        # Calculate background rectangle position
        bg_x1 = bbox[0]
        bg_y1 = bbox[1] - bg_height
        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,
            )

        # Center text in background rectangle
        text_x = bg_x1 + (bg_width - text_width) // 2
        text_y = bg_y1 + (bg_height + text_height) // 2

        cv2.putText(
            img,
            label,
            (text_x, text_y),
            font,
            size,
            text_color,
            thickness,
        )
    else:
        # Calculate background rectangle dimensions
        bg_width = text_width + 2 * padding
        bg_height = text_height + 2 * padding

        # Calculate background rectangle position
        bg_x1 = bbox[0]
        bg_y1 = 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,
            )

        # Center text in background rectangle
        text_x = bg_x1 + (bg_width - text_width) // 2
        text_y = bg_y1 + (bg_height + text_height) // 2

        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: list[list[int]], 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) -> 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 list[list[int]]

List of bounding boxes, each containing [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

Returns:

Type Description
NDArray[uint8]

Image with all labels added

Source code in bbox_visualizer/core/labels.py
def add_multiple_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: list[list[int]],
    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,
) -> 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 containing [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)

    Returns:
        Image with all labels added

    """
    if not bboxes or not labels:
        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")

    _validate_color(text_bg_color)
    _validate_color(text_color)

    # Convert bboxes to numpy array for vectorized operations
    bboxes = np.array(bboxes)

    # Validate and modify all bboxes at once
    bboxes = np.array([_check_and_modify_bbox(bbox, img.shape) for bbox in bboxes])

    # Draw all labels using add_label
    output = img.copy()
    for label, bbox in zip(labels, bboxes):
        output = add_label(
            output,
            label,
            bbox.tolist(),
            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: list[int], 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)) -> 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 list[int]

List of [x_min, y_min, x_max, y_max] coordinates

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)

Returns:

Type Description
NDArray[uint8]

Image with added T-shaped label

Source code in bbox_visualizer/core/flags.py
def add_T_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: list[int],
    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),
) -> 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: List of [x_min, y_min, x_max, y_max] coordinates
        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)

    Returns:
        Image with added T-shaped label

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

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

    # draw rectangle with label
    y_bottom = y_top
    y_top = y_bottom - label_height - 2 * padding

    if y_top < 0:
        if not _should_suppress_warning():
            logging.warning(
                "Labelling style 'T' going out of frame. Falling back to normal labeling."
            )
        return add_label(img, label, bbox)

    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
    bg_height = label_height + 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)

    # Center text in background rectangle
    text_x = bg_x1 + (bg_width - label_width) // 2
    text_y = bg_y1 + (bg_height + label_height) // 2

    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: list[list[int]], draw_bg: bool = True, text_bg_color: tuple[int, int, int] = (255, 255, 255), text_color: tuple[int, int, int] = (0, 0, 0)) -> 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 list[list[int]]

List of bounding boxes, each containing [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)

Returns:

Type Description
NDArray[uint8]

Image with all T-shaped labels added

Source code in bbox_visualizer/core/flags.py
def add_multiple_T_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: list[list[int]],
    draw_bg: bool = True,
    text_bg_color: tuple[int, int, int] = (255, 255, 255),
    text_color: tuple[int, int, int] = (0, 0, 0),
) -> 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 containing [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)

    Returns:
        Image with all T-shaped labels added

    """
    if not bboxes or not labels:
        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")

    _validate_color(text_bg_color)
    _validate_color(text_color)
    for label, bbox in zip(labels, bboxes):
        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,
        )

    return img

draw_flag_with_label

draw_flag_with_label(img: NDArray[uint8], label: str, bbox: list[int], 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)) -> 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 list[int]

List of [x_min, y_min, x_max, y_max] coordinates

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)

Returns:

Type Description
NDArray[uint8]

Image with added flag label

Source code in bbox_visualizer/core/flags.py
def draw_flag_with_label(
    img: NDArray[np.uint8],
    label: str,
    bbox: list[int],
    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),
) -> 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: List of [x_min, y_min, x_max, y_max] coordinates
        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)

    Returns:
        Image with added flag label

    """
    _validate_color(line_color)
    _validate_color(text_bg_color)
    _validate_color(text_color)
    bbox = _check_and_modify_bbox(bbox, img.shape)
    (label_width, label_height), baseline = cv2.getTextSize(
        label, font, 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:
        if not _should_suppress_warning():
            logging.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)

    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:
        text_width = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 1, 2)[0][0]
        label_bg = [
            start_point[0],
            start_point[1],
            start_point[0] + text_width,
            start_point[1] + 30,
        ]
        cv2.rectangle(
            img,
            (label_bg[0], label_bg[1]),
            (label_bg[2] + 5, label_bg[3]),
            text_bg_color,
            -1,
        )
        cv2.putText(
            img,
            label,
            (start_point[0] + 5, start_point[1] - 5 + 30),
            font,
            size,
            text_color,
            thickness,
        )

    return img

draw_multiple_flags_with_labels

draw_multiple_flags_with_labels(img: NDArray[uint8], labels: list[str], bboxes: list[list[int]], 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)) -> 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 list[list[int]]

List of bounding boxes, each containing [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)

Returns:

Type Description
NDArray[uint8]

Image with all flag labels added

Source code in bbox_visualizer/core/flags.py
def draw_multiple_flags_with_labels(
    img: NDArray[np.uint8],
    labels: list[str],
    bboxes: list[list[int]],
    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),
) -> 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 containing [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)

    Returns:
        Image with all flag labels added

    """
    if not bboxes or not labels:
        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")

    _validate_color(line_color)
    _validate_color(text_bg_color)
    _validate_color(text_color)
    for label, bbox in zip(labels, bboxes):
        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,
        )

    return img