{"record":{"id":"92006bed582b3748","repo":"roboflow/supervision","slug":"gray2bgr-conversion-requires-a-two-dimensional-ima","errorCode":null,"errorMessage":"GRAY2BGR conversion requires a two-dimensional image","messagePattern":"GRAY2BGR conversion requires a two-dimensional image","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/_cv2/_color.py","lineNumber":30,"sourceCode":"from 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\n            return weighted.astype(np.uint8)\n        float_values = (\n            image[..., 0].astype(np.float64) * 0.114\n            + image[..., 1].astype(np.float64) * 0.587\n            + image[..., 2].astype(np.float64) * 0.299","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_color.py#L12-L48","documentation":"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.","triggerScenarios":"`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.","commonSituations":"Model output masks stored with a trailing 1-sized channel axis; preprocessing code that uniformly adds a channel axis then calls cvtColor.","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)`"],"exampleFix":"// before\nbgr = cv2.cvtColor(mask[..., None], cv2.COLOR_GRAY2BGR)  # (H, W, 1)\n\n// after\nbgr = cv2.cvtColor(np.squeeze(mask), cv2.COLOR_GRAY2BGR)","handlingStrategy":"type-guard","validationCode":"import numpy as np\n\ndef as_grayscale_2d(image):\n    \"\"\"Return a 2-D single-channel array for GRAY2BGR expansion.\"\"\"\n    arr = np.asarray(image)\n    if arr.ndim == 3 and arr.shape[-1] == 1:\n        arr = arr[..., 0]\n    if arr.ndim != 2:\n        raise ValueError(f\"expected 2-D grayscale, got shape {arr.shape}\")\n    return arr","typeGuard":"def is_grayscale_2d(image) -> bool:\n    \"\"\"GRAY2BGR requires a 2-D array.\"\"\"\n    return np.asarray(image).ndim == 2","tryCatchPattern":null,"preventionTips":["Squeeze trailing 1-sized channel axes before color conversions","Keep masks stored as plain (H, W) arrays"],"tags":["cv2-fallback","color-conversion","grayscale","shape-validation"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}