{"record":{"id":"58e2ec755546ff60","repo":"roboflow/supervision","slug":"image-must-be-a-numpy-ndarray-or-pil-image-image","errorCode":null,"errorMessage":"`image` must be a numpy.ndarray or PIL.Image.Image. Received {type(image)}","messagePattern":"`image` must be a numpy\\.ndarray or PIL\\.Image\\.Image\\. Received (.+?)","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/supervision/utils/image.py","lineNumber":223,"sourceCode":"    x_min, y_min, x_max, y_max = xyxy_arr.flatten()\n\n    if isinstance(image, np.ndarray):\n        height, width = image.shape[:2]\n        x_min = int(np.clip(x_min, 0, width))\n        y_min = int(np.clip(y_min, 0, height))\n        x_max = int(np.clip(x_max, 0, width))\n        y_max = int(np.clip(y_max, 0, height))\n        return image[y_min:y_max, x_min:x_max]\n\n    if isinstance(image, Image.Image):\n        width, height = image.size\n        x_min = int(np.clip(x_min, 0, width))\n        y_min = int(np.clip(y_min, 0, height))\n        x_max = int(np.clip(x_max, 0, width))\n        y_max = int(np.clip(y_max, 0, height))\n        return image.crop((float(x_min), float(y_min), float(x_max), float(y_max)))\n\n    raise TypeError(\n        f\"`image` must be a numpy.ndarray or PIL.Image.Image. Received {type(image)}\"\n    )\n\n\n@ensure_cv2_image_for_standalone_function\ndef scale_image(image: ImageType, scale_factor: float) -> ImageType:\n    \"\"\"\n    Scale image by given factor. Scale factor > 1.0 zooms in, < 1.0 zooms out.\n\n    Args:\n        image: The image to scale.\n        scale_factor: Factor by which to scale the image.\n\n    Returns:\n        Scaled image matching input\n            type.\n\n    Raises:","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/utils/image.py#L205-L241","documentation":"Raised by `sv.crop_image` when the `image` argument is neither a `numpy.ndarray` nor a `PIL.Image.Image`. The function dispatches on these two types (each has a different crop path) and has no behavior for anything else, so it fails fast with a TypeError naming the received type.","triggerScenarios":"Passing a file path string, a `torch.Tensor`, a `cv2.VideoCapture` frame proxy, or bytes to `sv.crop_image(image=..., xyxy=...)`.","commonSituations":"Loading with PIL/opencv elsewhere but passing the path by mistake; deep-learning pipelines handing raw tensors to a utility that expects numpy; reading bytes from an HTTP response without decoding first via `sv.load_image_from_url`/`cv2.imdecode`.","solutions":["Convert tensors: `image.detach().cpu().numpy()` before cropping.","Load paths first: `cv2.imread(path)` or `PIL.Image.open(path)`.","Decode bytes with `cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)`."],"exampleFix":"# before\ncrop = sv.crop_image(image='/data/frame.jpg', xyxy=box)\n# after\nimport cv2\ncrop = sv.crop_image(image=cv2.imread('/data/frame.jpg'), xyxy=box)","handlingStrategy":"type-guard","validationCode":"assert isinstance(image, (np.ndarray, Image.Image)), type(image)","typeGuard":"from PIL import Image\nimport numpy as np\n\ndef is_image(x: Any) -> bool:\n    return isinstance(x, (np.ndarray, Image.Image))","tryCatchPattern":"try:\n    crop = sv.crop_image(image, xyxy)\nexcept TypeError as e:\n    raise TypeError(f'load the image first: {e}') from e","preventionTips":["Keep one canonical loaded representation (ndarray BGR) through the pipeline.","Convert tensors with .detach().cpu().numpy() at model boundaries.","Never pass paths or raw bytes to image utilities."],"tags":["image","crop","type-guard","numpy","pillow"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}