{"record":{"id":"20ce318ae7783790","repo":"docling-project/docling","slug":"unsupported-numpy-array-shape-image-shape","errorCode":null,"errorMessage":"Unsupported numpy array shape: {image.shape}","messagePattern":"Unsupported numpy array shape: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/models/inference_engines/vlm/_utils.py","lineNumber":41,"sourceCode":"    Args:\n        image: Input image as PIL Image or numpy array\n\n    Returns:\n        RGB PIL Image\n\n    Raises:\n        ValueError: If numpy array has unsupported shape\n    \"\"\"\n    # Handle numpy arrays\n    if isinstance(image, np.ndarray):\n        if image.ndim == 3 and image.shape[2] in [3, 4]:\n            # RGB or RGBA array\n            image = Image.fromarray(image.astype(np.uint8))\n        elif image.ndim == 2:\n            # Grayscale array\n            image = Image.fromarray(image.astype(np.uint8), mode=\"L\")\n        else:\n            raise ValueError(f\"Unsupported numpy array shape: {image.shape}\")\n\n    # Ensure RGB mode (handles RGBA, L, P, etc.)\n    if image.mode != \"RGB\":\n        image = image.convert(\"RGB\")\n\n    return image\n\n\ndef preprocess_image_batch(\n    images: List[Union[Image.Image, np.ndarray]],\n) -> List[Image.Image]:\n    \"\"\"Preprocess a batch of images to RGB PIL Images.\n\n    Args:\n        images: List of images as PIL Images or numpy arrays\n\n    Returns:\n        List of RGB PIL Images","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/models/inference_engines/vlm/_utils.py#L23-L59","documentation":"In the VLM image preprocessing utility, a numpy array is accepted only with ndim==2 (grayscale) or ndim==3 with last dimension 3 or 4 (RGB/RGBA). Any other shape (e.g. 3 dims with shape[2]==1, or 4+ dims) raises ValueError('Unsupported numpy array shape').","triggerScenarios":"Passing numpy images to a VLM engine predict call where arrays come from cv2 with BGR + extra channel, single-channel arrays kept as (H, W, 1), float arrays with an unexpected axis, or batched arrays of shape (N, H, W, 3).","commonSituations":"cv2.imread with IMREAD_UNCHANGED on grayscale PNGs producing (H, W, 1); medical/scientific imaging pipelines with (H, W, 1) or (H, W, k>4) multi-channel stacks; passing batched tensors instead of per-image arrays.","solutions":["Squeeze singleton channel dims before passing: arr = arr.squeeze() or arr = arr[:, :, 0] for (H, W, 1).","Convert to a PIL Image yourself (Image.fromarray(arr).convert('RGB')) and pass PIL images instead.","For BGR cv2 arrays, cv2.cvtColor(arr, cv2.COLOR_BGR2RGB) first — and ensure ndim/shape fit the accepted forms."],"exampleFix":"# before\nimage = cv2.imread(path, cv2.IMREAD_UNCHANGED)  # may be (H, W, 1) or BGRA\noutputs = vlm_engine.predict_batch([{\"image\": image, \"prompt\": p}])\n\n# after\nimage = cv2.imread(path)\nif image.ndim == 2:\n    pass\nelif image.ndim == 3 and image.shape[2] == 1:\n    image = image[:, :, 0]\nelse:\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\noutputs = vlm_engine.predict_batch([{\"image\": image, \"prompt\": p}])","handlingStrategy":"type-guard","validationCode":"def is_acceptable_array(a: \"np.ndarray\") -> bool:\n    return a.ndim == 2 or (a.ndim == 3 and a.shape[2] in (3, 4))\n\nif isinstance(img, np.ndarray) and not is_acceptable_array(img):\n    img = np.squeeze(img)  # or convert explicitly before passing","typeGuard":"def is_vlm_ready_image(x: object) -> TypeGuard[Union[Image.Image, \"np.ndarray\"]]:\n    if isinstance(x, Image.Image):\n        return True\n    return isinstance(x, np.ndarray) and (x.ndim == 2 or (x.ndim == 3 and x.shape[2] in (3, 4)))","tryCatchPattern":"try:\n    images = preprocess_image_batch(raw_images)\nexcept ValueError as e:\n    raise ValueError(f\"Normalize arrays to HxW, HxWx3 or HxWx4 before VLM inference: {e}\") from e","preventionTips":["Standardize on PIL RGB images at your API boundary.","For cv2 sources, always cvtColor to RGB and squeeze singleton channels.","Validate array ndim/shape in a helper before building the batch."],"tags":["numpy","image-preprocessing","vlm","validation","shape"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}