roboflow/supervision · error · ValueError

mode must be 'edge' or 'centroid'

Error message

mode must be 'edge' or 'centroid'

What it means

The mask cleanup helper supports exactly two distance modes: 'centroid' (distance between component centroids) and 'edge' (distance between component edges via Chamfer-style distances). Any other string — or a non-string value — reaches the final else branch and raises.

Source

Thrown at src/supervision/detection/utils/masks.py:512

        main_mask = labels == main_label
        if np.isnan(threshold) or threshold < 0:
            nearby_main = np.zeros_like(main_mask)
        elif np.isposinf(threshold):
            nearby_main = np.ones_like(main_mask)
        else:
            fixed_distances = _chamfer_distances(main_mask)
            distances = fixed_distances.astype(np.float32) / 65536
            nearby_main = distances <= threshold
        for label in range(1, num_labels):
            if label == main_label:
                continue
            component = labels == label
            if not np.any(component):
                continue
            if np.any(nearby_main & component):
                keep_labels[label] = True
    else:
        raise ValueError("mode must be 'edge' or 'centroid'")

    return keep_labels[labels]


def mask_to_roi(mask: npt.NDArray[np.bool_]) -> tuple[int, int, int, int] | None:
    """Return exclusive ``(x1, y1, x2, y2)`` bounds for true mask pixels.

    Use this helper when you need NumPy slice semantics. Unlike
    :func:`~supervision.detection.utils.converters.mask_to_xyxy`, this
    function uses exclusive upper bounds (``+1``) and returns ``None`` for
    empty masks instead of zeros. The inclusive ``mask_to_xyxy`` convention
    stays in place for compatibility with CompactMask and box-based adapters.

    Args:
        mask: 2D boolean array of shape ``(H, W)``.

    Returns:
        Exclusive ``(x1, y1, x2, y2)`` bounds, or ``None`` when the mask

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Use exactly 'centroid' or 'edge' (lowercase).
  2. Normalize config values: mode = cfg['mode'].strip().lower().
  3. Validate mode at the config boundary with an explicit error listing the two options.

Example fix

# before
 filtered = filter_non_zero_mask_areas(mask=m, relative_distance=0.2, mode="center")

# after
 filtered = filter_non_zero_mask_areas(mask=m, relative_distance=0.2, mode="centroid")
Defensive patterns

Strategy: validation

Validate before calling

mode = mode.strip().lower()
if mode not in ("edge", "centroid"):
    raise ValueError(f"mode must be 'edge' or 'centroid', got {mode!r}")

Type guard

def is_valid_mode(mode: str) -> bool:
    return mode.strip().lower() in ("edge", "centroid")

Prevention

When it happens

Trigger: Passing mode='center' (common typo for 'centroid'), mode='Edge' (case-sensitive), or a config value with whitespace like 'edge ' read from a file.

Common situations: Config/CLI values not normalized (case, whitespace); assuming sklearn-style naming ('distance'); typos between 'centroid' and 'center'.

Related errors


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