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
- Reshape points to (N, 2): `np.asarray(pts).reshape(-1, 2)`
- Drop the extra column before drawing: `pts[:, :2]` for (N, 3) inputs
- 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
- Standardize on (N, 2) float/int arrays for all polygon/keypoint drawing
- In cv2-optional deployments, run supervisions's import once and check for the fallback warning
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
- Only unshifted drawing coordinates are supported
- Only None hierarchy is supported by the fallback
- Contour input must be a two-dimensional image
- BGR/RGB conversion requires a three-channel image
- GRAY2BGR conversion requires a two-dimensional image
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/74cd146f9834b288.
Report an issue: GitHub.