{"record":{"id":"6058b3696ea4fd05","repo":"roboflow/supervision","slug":"image-must-have-3-or-4-channels","errorCode":null,"errorMessage":"Image must have 3 or 4 channels.","messagePattern":"Image must have 3 or 4 channels\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/draw/utils.py","lineNumber":448,"sourceCode":"        >>> scene.shape\n        (100, 100, 3)\n\n        ```\n    \"\"\"\n\n    # Validate and load image\n    if isinstance(image, str):\n        if not os.path.exists(image):\n            raise FileNotFoundError(f\"Image path ('{image}') does not exist.\")\n        loaded_image = cv2.imread(image, cv2.IMREAD_UNCHANGED)\n        if loaded_image is None:\n            raise OSError(f\"Could not decode image path ('{image}').\")\n        image_np = cast(npt.NDArray[np.uint8], loaded_image)\n    else:\n        image_np = image\n\n    if image_np.ndim != 3 or image_np.shape[2] not in (3, 4):\n        raise ValueError(\"Image must have 3 or 4 channels.\")\n\n    # Validate opacity\n    if not 0.0 <= opacity <= 1.0:\n        raise ValueError(\"Opacity must be between 0.0 and 1.0.\")\n\n    rect_x = int(rect.x)\n    rect_y = int(rect.y)\n    rect_width = int(rect.width)\n    rect_height = int(rect.height)\n    # Validate rectangle dimensions\n    if (\n        rect_x < 0\n        or rect_y < 0\n        or rect_x + rect_width > scene.shape[1]\n        or rect_y + rect_height > scene.shape[0]\n    ):\n        raise ValueError(\"Invalid rectangle dimensions.\")\n","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/draw/utils.py#L430-L466","documentation":"Raised by draw_image (draw/utils.py) when the image to paste is not a 3-D array with exactly 3 or 4 channels. The function supports RGB/BGR (3-channel) and RGBA/BGRA (4-channel, using the alpha channel for blending); grayscale, single-channel, or multi-frame tensors are rejected before any resizing happens.","triggerScenarios":"Calling draw_image(scene, image, ...) with a (H, W) grayscale array, a (H, W, 1) array, or an (N, H, W, 3) batched tensor.","commonSituations":"Loading an image with cv2.imread(path, cv2.IMREAD_GRAYSCALE) or cv2.IMREAD_UNCHANGED on a PNG with transparency already stripped; palette/label images generated programmatically as 2-D arrays; passing a torch tensor without converting to HWC numpy.","solutions":["Convert grayscale to BGR: cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)","Load color images without IMREAD_GRAYSCALE: cv2.imread(path) yields 3 channels","Convert framework tensors to HWC uint8 numpy before calling (e.g. tensor.permute(1,2,0).numpy())"],"exampleFix":"# before\nlogo = cv2.imread(path, cv2.IMREAD_GRAYSCALE)  # (H, W)\nscene = draw_image(scene, logo, opacity=0.8, rect=rect)\n\n# after\nlogo = cv2.imread(path, cv2.IMREAD_UNCHANGED)  # (H, W, 3|4)\nscene = draw_image(scene, logo, opacity=0.8, rect=rect)","handlingStrategy":"validation","validationCode":"import cv2\n\nimage = np.asarray(image)\nif image.ndim == 2:\n    image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)\nif image.ndim != 3 or image.shape[2] not in (3, 4):\n    raise ValueError(f'unsupported image shape {image.shape}')\nscene = draw_image(scene, image, opacity=opacity, rect=rect)","typeGuard":"import numpy as np\n\ndef is_blendable_image(img: np.ndarray) -> bool:\n    \"\"\"True when img is (H, W, 3|4) uint8-like, as draw_image requires.\"\"\"\n    return img.ndim == 3 and img.shape[2] in (3, 4)","tryCatchPattern":"try:\n    scene = draw_image(scene, image, opacity=opacity, rect=rect)\nexcept ValueError as e:\n    if '3 or 4 channels' in str(e):\n        scene = draw_image(scene, cv2.cvtColor(image, cv2.COLOR_GRAY2BGR), opacity=opacity, rect=rect)\n    else:\n        raise","preventionTips":["Never use cv2.IMREAD_GRAYSCALE for assets destined for draw_image","Convert framework tensors (CHW) to HWC numpy before drawing"],"tags":["drawing","image","shape-validation","opencv"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}