{"record":{"id":"641bab306ac687ad","repo":"roboflow/supervision","slug":"bgr-rgb-conversion-requires-a-three-channel-image","errorCode":null,"errorMessage":"BGR/RGB conversion requires a three-channel image","messagePattern":"BGR/RGB conversion requires a three-channel image","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/_cv2/_color.py","lineNumber":25,"sourceCode":"\nimport numpy as np\nimport numpy.typing as npt\n\nfrom supervision._cv2._common import _cast_array_like_opencv\nfrom supervision._cv2.constants import (\n    _COLOR_BGR2GRAY,\n    _COLOR_BGR2RGB,\n    _COLOR_GRAY2BGR,\n    _COLOR_HSV2BGR,\n    _COLOR_RGB2BGR,\n)\n\n\ndef _cvt_color(image: npt.NDArray[Any], code: int) -> npt.NDArray[Any]:\n    \"\"\"Convert the BGR, RGB, grayscale, and 8-bit HSV formats used by Supervision.\"\"\"\n    if code in (_COLOR_BGR2RGB, _COLOR_RGB2BGR):\n        if image.ndim != 3 or image.shape[2] != 3:\n            raise ValueError(\"BGR/RGB conversion requires a three-channel image\")\n        return np.ascontiguousarray(image[..., ::-1])\n\n    if code == _COLOR_GRAY2BGR:\n        if image.ndim != 2:\n            raise ValueError(\"GRAY2BGR conversion requires a two-dimensional image\")\n        return np.repeat(image[..., np.newaxis], 3, axis=2)\n\n    if code == _COLOR_BGR2GRAY:\n        if image.ndim != 3 or image.shape[2] != 3:\n            raise ValueError(\"BGR2GRAY conversion requires a three-channel image\")\n        if image.dtype == np.uint8:\n            values = image.astype(np.uint32)\n            weighted = (\n                values[..., 0] * 3735\n                + values[..., 1] * 19235\n                + values[..., 2] * 9798\n                + (1 << 14)\n            ) >> 15","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_color.py#L7-L43","documentation":"BGR<->RGB conversion is a channel reversal (`image[..., ::-1]`) and only makes sense for 3-channel images. The fallback at src/supervision/_cv2/_color.py:25 raises when `image.ndim != 3 or image.shape[2] != 3` — e.g. passing an RGBA (4-channel) image, a grayscale 2-D image, or a batched (N, H, W, 3) array.","triggerScenarios":"`cv2.cvtColor(img, cv2.COLOR_BGR2RGB)` on an RGBA image loaded with IMREAD_UNCHANGED, a 2-D grayscale array, or a 4-D batched tensor converted to ndarray.","commonSituations":"PNG with alpha channel loaded via `cv2.imread(path, cv2.IMREAD_UNCHANGED)`; webcams that yield RGBA; model preprocessing that assumes 3 channels receiving grayscale input.","solutions":["Slice to 3 channels first: `img = img[..., :3]` for RGBA","For grayscale input use COLOR_GRAY2BGR first if you need 3 channels","Index the batch dimension for 4-D arrays: `images[i]`"],"exampleFix":"// before\nrgb = cv2.cvtColor(rgba_frame, cv2.COLOR_BGR2RGB)  # shape (H, W, 4)\n\n// after\nrgb = cv2.cvtColor(rgba_frame[..., :3], cv2.COLOR_BGR2RGB)","handlingStrategy":"type-guard","validationCode":"import numpy as np\n\ndef as_three_channel(image):\n    \"\"\"Return a (H, W, 3) array for channel-swap conversions.\"\"\"\n    arr = np.asarray(image)\n    if arr.ndim == 2:\n        arr = np.repeat(arr[..., None], 3, axis=2)\n    elif arr.ndim == 3 and arr.shape[2] > 3:\n        arr = arr[..., :3]\n    if arr.ndim != 3 or arr.shape[2] != 3:\n        raise ValueError(f\"expected (H, W, 3), got {arr.shape}\")\n    return arr","typeGuard":"def is_three_channel(image) -> bool:\n    \"\"\"BGR/RGB conversion requires exactly (H, W, 3).\"\"\"\n    arr = np.asarray(image)\n    return arr.ndim == 3 and arr.shape[2] == 3","tryCatchPattern":null,"preventionTips":["Slice alpha away after IMREAD_UNCHANGED loads","Standardize frames to 3 channels at ingestion so downstream cvtColor calls are safe"],"tags":["cv2-fallback","color-conversion","channels","shape-validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}