{"record":{"id":"1796c1ad5623d5c3","repo":"invoke-ai/InvokeAI","slug":"invalid-number-of-channels","errorCode":null,"errorMessage":"Invalid number of channels.","messagePattern":"Invalid number of channels\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"invokeai/backend/image_util/util.py","lineNumber":137,"sourceCode":"    \"\"\"\n    assert image.dtype == np.uint8\n    if image.ndim == 2:\n        image = image[:, :, None]\n    assert image.ndim == 3\n    _height, _width, channels = image.shape\n    assert channels == 1 or channels == 3 or channels == 4\n    if channels == 3:\n        return image\n    if channels == 1:\n        return np.concatenate([image, image, image], axis=2)\n    if channels == 4:\n        color = image[:, :, 0:3].astype(np.float32)\n        alpha = image[:, :, 3:4].astype(np.float32) / 255.0\n        normalized = color * alpha + 255.0 * (1.0 - alpha)\n        normalized = normalized.clip(0, 255).astype(np.uint8)\n        return normalized\n\n    raise ValueError(\"Invalid number of channels.\")\n\n\ndef resize_image_to_resolution(input_image: np.ndarray, resolution: int) -> np.ndarray:\n    \"\"\"Resizes an image, fitting it to the given resolution.\n\n    Adapted from https://github.com/huggingface/controlnet_aux (Apache-2.0 license).\n\n    Args:\n        input_image: The input image.\n        resolution: The resolution to fit the image to.\n\n    Returns:\n        The resized image.\n    \"\"\"\n    h = float(input_image.shape[0])\n    w = float(input_image.shape[1])\n    scaling_factor = float(resolution) / min(h, w)\n    h = int(h * scaling_factor)","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/backend/image_util/util.py#L119-L155","documentation":"normalize_image_channel_count converts numpy image arrays to 3 channels: 1-channel is triplicated, 4-channel RGBA is alpha-blended onto white, 3-channel passes through. It raises this ValueError only if channels is not in {1,3,4} — e.g. a 2-channel or >4-channel array slipped past the caller. Note the code asserts channels in {1,3,4} just above, so hitting the raise usually means a build with -O0/asserts stripped or a shape confusion.","triggerScenarios":"Passing a numpy array with 2 channels (e.g. cv2 grayscale loaded with an odd flag or a (H,W,2) array), 5+ channels, or an accidental shape mismatch such as stacking two single-channel images. Called via np_img_resize, get_canny_edges, and controlnet run paths.","commonSituations":"Feeding video frames or exotic formats (e.g. YUV, 16-bit multi-channel) into controlnet preprocessing, concatenating arrays incorrectly, or an OpenCV load returning an unexpected channel layout (IMREAD_UNCHANGED on a 2-channel PNG).","solutions":["Convert the input to RGB or grayscale before calling: img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) or load with cv2.IMREAD_COLOR.","Check image.shape — if the last dimension is not 1, 3, or 4, reshape or drop the extra channels before calling.","If the array is (H,W,2), inspect the pipeline step that produced it; usually one channel is data and one is alpha — split or merge them appropriately."],"exampleFix":"// before\nedges = get_canny_edges(odd_array)  # odd_array.shape == (H, W, 2)\n// after\nassert odd_array.ndim == 3 and odd_array.shape[2] in (1, 3, 4)\nrgb = cv2.cvtColor(odd_array, cv2.COLOR_BGR2RGB) if odd_array.shape[2] == 3 else odd_array[:, :, :3]\nedges = get_canny_edges(rgb)","handlingStrategy":"type-guard","validationCode":"def ensure_rgb_uint8(img: np.ndarray) -> np.ndarray:\n    assert img.dtype == np.uint8\n    if img.ndim == 2:\n        img = img[:, :, None]\n    if img.shape[2] not in (1, 3, 4):\n        raise ValueError(f\"expected 1/3/4 channels, got {img.shape[2]}\")\n    return img","typeGuard":"def is_normalizable_image(img: np.ndarray) -> bool:\n    return img.dtype == np.uint8 and img.ndim == 3 and img.shape[2] in (1, 3, 4)","tryCatchPattern":"try:\n    normalized = normalize_image_channel_count(img)\nexcept ValueError:\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # or inspect img.shape and fix channels\n    normalized = normalize_image_channel_count(img)","preventionTips":["Log/inspect img.shape before preprocessing; expect (H, W, 3) for typical RGB sources.","Avoid cv2.IMREAD_UNCHANGED unless you handle alpha/16-bit layouts yourself.","Convert all inputs to RGB uint8 at the ingestion boundary of your pipeline, once."],"tags":["numpy","image","shape","validation"],"backgroundTag":"invalid-image-channel-count","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}