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
- Use exactly 'inclusive' or 'exclusive' (lowercase strings).
- Pick 'exclusive' if you will use the bounds as slice end indices, 'inclusive' if you compare against pixel coordinates.
- 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
- Use 'exclusive' when bounds feed NumPy slices; 'inclusive' for pixel-coordinate math.
- Keep a single CONVENTION constant shared across encode/decode calls.
- Normalize config strings before passing.
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
- Incorrect connectivity value. Possible connectivity values:
- Either absolute_distance or relative_distance must be set.
- mode must be 'edge' or 'centroid'
- masks_true and masks_detection must be 3D (N, H, W); got ndi
- MeanAverageRecall with `MetricTarget.MASKS` requires detecti
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/7334da8676b8626c.
Report an issue: GitHub.