roboflow/supervision · error · ValueError

Only BORDER_CONSTANT is supported by the fallback

Error message

Only BORDER_CONSTANT is supported by the fallback

What it means

The fallback `cv2.copyMakeBorder` at src/supervision/_cv2/_image.py:45 implements only BORDER_CONSTANT (padding with a fixed value) because that is all supervision uses. Border modes like BORDER_REFLECT, BORDER_REPLICATE, or BORDER_WRAP require edge-mirroring logic the fallback does not provide, so they are rejected up front.

Source

Thrown at src/supervision/_cv2/_image.py:45

    elif flip_code == -1:
        axes = (0, 1)
    else:
        raise ValueError(f"Unsupported flip code: {flip_code}")
    return np.ascontiguousarray(np.flip(image, axis=axes))


def _copy_make_border(
    image: npt.NDArray[Any],
    top: int,
    bottom: int,
    left: int,
    right: int,
    border_type: int,
    value: int | float | Sequence[int | float] = 0,
) -> npt.NDArray[Any]:
    """Add a constant border around an image."""
    if border_type != _BORDER_CONSTANT:
        raise ValueError("Only BORDER_CONSTANT is supported by the fallback")
    if min(top, bottom, left, right) < 0:
        raise ValueError("Border sizes must be non-negative")

    height, width = image.shape[:2]
    shape = (height + top + bottom, width + left + right, *image.shape[2:])

    # OpenCV's Scalar(v) fills only channel 0 and zero-pads the rest for
    # multichannel images — a bare scalar is treated the same as a
    # length-1 sequence, not broadcast to every channel.
    sequence_value = value if isinstance(value, Sequence) else (value,)
    values = np.asarray(sequence_value, dtype=image.dtype).reshape(-1)
    if image.ndim == 2:
        fill_value: Any = values[0] if values.size else 0
    else:
        fill = np.zeros(image.shape[2], dtype=image.dtype)
        fill[: min(values.size, image.shape[2])] = values[: image.shape[2]]
        fill_value = fill.reshape((1, 1, -1))

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Use `cv2.BORDER_CONSTANT` with an explicit fill `value`
  2. Implement reflect/replicate padding yourself with `np.pad` mode equivalents (`mode='reflect'`, `'edge'`)
  3. Install `opencv-python` for the full set of border modes

Example fix

// before
padded = cv2.copyMakeBorder(img, 10, 10, 10, 10, cv2.BORDER_REFLECT)

// after
padded = np.pad(img, ((10, 10), (10, 10)) + ((0, 0),) * (img.ndim - 2), mode='reflect')
Defensive patterns

Strategy: fallback

Validate before calling

import numpy as np

BORDER_MODES = {"constant": None, "reflect": "reflect", "reflect_101": "reflect", "replicate": "edge", "wrap": "wrap"}

def pad_image(image, top, bottom, left, right, mode: str = "constant", value=0):
    """Portable padding: BORDER_CONSTANT via fallback, others via np.pad."""
    if mode == "constant":
        return cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT, value=value)
    np_mode = BORDER_MODES[mode]
    pad_width = ((top, bottom), (left, right)) + ((0, 0),) * (np.asarray(image).ndim - 2)
    return np.pad(image, pad_width, mode=np_mode)

Prevention

When it happens

Trigger: Calling `cv2.copyMakeBorder(img, t, b, l, r, cv2.BORDER_REFLECT)` (or any non-constant border type) while running without opencv-python, e.g. in data-augmentation or letterboxing code.

Common situations: Augmentation pipelines ported from training code that use reflective padding; letterbox resize helpers using BORDER_REFLECT_101 (the OpenCV default is BORDER_CONSTANT|BORDER_ISOLATED variants differ by call).

Related errors


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