roboflow/supervision · error · ValueError

coordinate_convention must be 'inclusive' or 'exclusive', go

Error message

coordinate_convention must be 'inclusive' or 'exclusive', got {coordinate_convention}.

What it means

masks_to_xyxy_bounds converts boolean masks to bounding boxes under one of two conventions: 'inclusive' (x_max is the last foreground column, COCO-style) or 'exclusive' (x_max is one past the last column, slice-friendly). Any other string reaches the else branch and raises, because the two conventions produce different coordinates and the caller must choose explicitly.

Source

Thrown at src/supervision/detection/utils/converters.py:280

        return np.zeros((n, 4), dtype=int)

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

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

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

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


def xyxy_to_mask(
    boxes: npt.NDArray[np.number],
    resolution_wh: tuple[int, int],
    coordinate_convention: CoordinateConvention = "inclusive",
) -> npt.NDArray[np.bool_]:
    """
    Converts a 2D `np.ndarray` of bounding boxes into a 3D `np.ndarray` of bool masks.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Use exactly 'inclusive' or 'exclusive' (lowercase strings).
  2. Pick 'exclusive' if you will use the bounds as slice end indices, 'inclusive' if you compare against pixel coordinates.
  3. Normalize any config-sourced value with .strip().lower().

Example fix

# before
 xyxy = sv.masks_to_xyxy_bounds(masks=masks, coordinate_convention="Inclusive")

# after
 xyxy = sv.masks_to_xyxy_bounds(masks=masks, coordinate_convention="inclusive")
Defensive patterns

Strategy: validation

Validate before calling

coordinate_convention = coordinate_convention.strip().lower()
if coordinate_convention not in ("inclusive", "exclusive"):
    raise ValueError(f"bad convention {coordinate_convention!r}")

Type guard

def is_valid_convention(value: str) -> bool:
    return value.strip().lower() in ("inclusive", "exclusive")

Prevention

When it happens

Trigger: Passing coordinate_convention='Inclusive' (capitalized), 'incl', or omitting it where no default exists in the code path; forwarding a config value with a typo.

Common situations: Bridging COCO annotations (inclusive) with NumPy slicing (exclusive) and guessing the keyword; case-sensitive config values; renaming the parameter in older code.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/7334da8676b8626c. Report an issue: GitHub.