roboflow/supervision · error · ValueError
GRAY2BGR conversion requires a two-dimensional image
Error message
GRAY2BGR conversion requires a two-dimensional image
What it means
GRAY2BGR expands a 2-D single-channel image to 3 channels by repetition. The fallback at src/supervision/_cv2/_color.py:30 requires `image.ndim == 2`; a 3-D (H, W, 1) array or a batched (N, H, W) array is rejected because the expansion axis is ambiguous.
Source
Thrown at src/supervision/_cv2/_color.py:30
from supervision._cv2.constants import (
_COLOR_BGR2GRAY,
_COLOR_BGR2RGB,
_COLOR_GRAY2BGR,
_COLOR_HSV2BGR,
_COLOR_RGB2BGR,
)
def _cvt_color(image: npt.NDArray[Any], code: int) -> npt.NDArray[Any]:
"""Convert the BGR, RGB, grayscale, and 8-bit HSV formats used by Supervision."""
if code in (_COLOR_BGR2RGB, _COLOR_RGB2BGR):
if image.ndim != 3 or image.shape[2] != 3:
raise ValueError("BGR/RGB conversion requires a three-channel image")
return np.ascontiguousarray(image[..., ::-1])
if code == _COLOR_GRAY2BGR:
if image.ndim != 2:
raise ValueError("GRAY2BGR conversion requires a two-dimensional image")
return np.repeat(image[..., np.newaxis], 3, axis=2)
if code == _COLOR_BGR2GRAY:
if image.ndim != 3 or image.shape[2] != 3:
raise ValueError("BGR2GRAY conversion requires a three-channel image")
if image.dtype == np.uint8:
values = image.astype(np.uint32)
weighted = (
values[..., 0] * 3735
+ values[..., 1] * 19235
+ values[..., 2] * 9798
+ (1 << 14)
) >> 15
return weighted.astype(np.uint8)
float_values = (
image[..., 0].astype(np.float64) * 0.114
+ image[..., 1].astype(np.float64) * 0.587
+ image[..., 2].astype(np.float64) * 0.299View on GitHub (pinned to 7f254d9784)
Solutions
- Squeeze to 2-D first: `gray = gray.squeeze()` (or `gray[..., 0]`)
- Index the batch for (N, H, W): `gray = batch[i]`
- Build the 3-channel array directly: `np.repeat(gray[..., None], 3, axis=2)`
Example fix
// before bgr = cv2.cvtColor(mask[..., None], cv2.COLOR_GRAY2BGR) # (H, W, 1) // after bgr = cv2.cvtColor(np.squeeze(mask), cv2.COLOR_GRAY2BGR)
Defensive patterns
Strategy: type-guard
Validate before calling
import numpy as np
def as_grayscale_2d(image):
"""Return a 2-D single-channel array for GRAY2BGR expansion."""
arr = np.asarray(image)
if arr.ndim == 3 and arr.shape[-1] == 1:
arr = arr[..., 0]
if arr.ndim != 2:
raise ValueError(f"expected 2-D grayscale, got shape {arr.shape}")
return arr Type guard
def is_grayscale_2d(image) -> bool:
"""GRAY2BGR requires a 2-D array."""
return np.asarray(image).ndim == 2 Prevention
- Squeeze trailing 1-sized channel axes before color conversions
- Keep masks stored as plain (H, W) arrays
When it happens
Trigger: `cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)` where gray has shape (H, W, 1) (common after `np.expand_dims`/`[..., None]`) or (N, H, W) from a batch.
Common situations: Model output masks stored with a trailing 1-sized channel axis; preprocessing code that uniformly adds a channel axis then calls cvtColor.
Related errors
- BGR/RGB conversion requires a three-channel image
- BGR2GRAY conversion requires a three-channel image
- HSV2BGR conversion requires a three-channel image
- Drawing points must have shape (N, 2) or (N, 1, 2)
- Contour input must be a two-dimensional image
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/92006bed582b3748.
Report an issue: GitHub.