roboflow/supervision · error · ValueError

Drawing points must have shape (N, 2) or (N, 1, 2)

Error message

Drawing points must have shape (N, 2) or (N, 1, 2)

What it means

supervision ships a pure NumPy/Pillow fallback used when `opencv-python` is not installed. `_points` in src/supervision/_cv2/_drawing.py:56 normalizes point arrays for the Pillow rasterizer and only accepts shapes `(N, 2)` or `(N, 1, 2)` — the shapes OpenCV drawing functions produce. Anything else (1-D, (N, 3), (N, 2, 1), non-numeric) raises ValueError.

Source

Thrown at src/supervision/_cv2/_drawing.py:56

def _paint(image: _ImageArray, mask: npt.NDArray[np.bool_], color: Any) -> _ImageArray:
    """Apply a scalar or multi-channel color to a drawing mask."""
    image[mask] = _color_for_image(image, color)
    return image


def _point(point: Sequence[int | float]) -> _Point:
    """Convert an OpenCV point to integer Pillow coordinates."""
    return round(point[0]), round(point[1])


def _points(points: npt.NDArray[Any], offset: tuple[int, int] = (0, 0)) -> list[_Point]:
    """Normalize OpenCV polygon shapes to integer Pillow coordinates."""
    values = np.asarray(points)
    if values.size == 0:
        return []
    if values.ndim not in (2, 3) or values.shape[-1] != 2:
        raise ValueError("Drawing points must have shape (N, 2) or (N, 1, 2)")
    normalized = np.rint(values.reshape(-1, 2)).astype(np.int64)
    normalized += np.asarray(offset, dtype=np.int64)
    return [(int(x), int(y)) for x, y in normalized]


def _validate_shift(shift: int) -> None:
    """Reject fixed-point coordinates not supported by the fallback."""
    if shift != 0:
        raise ValueError("Only unshifted drawing coordinates are supported")


def _line(
    img: _ImageArray,
    pt1: Sequence[int | float],
    pt2: Sequence[int | float],
    color: Any,
    thickness: int = 1,
    lineType: int = 8,

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Reshape points to (N, 2): `np.asarray(pts).reshape(-1, 2)`
  2. Drop the extra column before drawing: `pts[:, :2]` for (N, 3) inputs
  3. Install `opencv-python` so the real cv2 backend handles its usual flexible inputs

Example fix

// before
points = np.array([[10, 20, 0], [30, 40, 0]])  # (N, 3)
scene = sv.draw_polygon(scene, points)

// after
points = np.asarray(points).reshape(-1, 2)[:, :2]
scene = sv.draw_polygon(scene, points)
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np

def as_drawable_points(points) -> np.ndarray:
    """Coerce point input to the (N, 2) shape drawing APIs require."""
    arr = np.asarray(points)
    if arr.ndim not in (2, 3) or arr.shape[-1] != 2:
        arr = arr.reshape(-1, 2)[:, :2]
    return arr

Type guard

def is_drawable_points(points) -> bool:
    """Drawing fallbacks accept only (N, 2) or (N, 1, 2) arrays."""
    arr = np.asarray(points)
    return arr.ndim in (2, 3) and arr.shape[-1] == 2

Prevention

When it happens

Trigger: Running without opencv installed while annotators or `cv2.polylines`/`fillPoly`/`drawContours` fallbacks receive raw point arrays: passing a flat `[x1, y1, x2, y2]` list, an (N, 3) xyz array, or an (N, 1, 3) array from a mesh/pose pipeline.

Common situations: Minimal deployments (Docker slim images, serverless) that omit opencv-python; passing keypoints from a 3D pose model or polygon vertices stored as (N, 3) directly to an annotator that forwards them to drawing.

Related errors


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