{"record":{"id":"d2ba4b2cf2e536b1","repo":"opendatalab/MinerU","slug":"input-must-be-a-pillow-object-or-a-numpy-array","errorCode":null,"errorMessage":"Input must be a pillow object or a numpy array.","messagePattern":"Input must be a pillow object or a numpy array\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mineru/model/table/rec/unet_table/main.py","lineNumber":280,"sourceCode":"    td_count = html_lower.count('<td')\n    th_count = html_lower.count('<th')\n    return td_count + th_count\n\n\nclass UnetTableModel:\n    def __init__(self, ocr_engine):\n        model_path = os.path.join(auto_download_and_get_model_root_path(ModelPath.unet_structure), ModelPath.unet_structure)\n        wired_input_args = WiredTableInput(model_path=model_path)\n        self.wired_table_model = WiredTableRecognition(wired_input_args, ocr_engine)\n        self.ocr_engine = ocr_engine\n\n    def predict(self, input_img, ocr_result, wireless_html_code, return_metadata: bool = False):\n        if isinstance(input_img, Image.Image):\n            np_img = np.asarray(input_img)\n        elif isinstance(input_img, np.ndarray):\n            np_img = input_img\n        else:\n            raise ValueError(\"Input must be a pillow object or a numpy array.\")\n        bgr_img = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)\n\n        if ocr_result is None:\n            ocr_result = self.ocr_engine.ocr(bgr_img)[0]\n            ocr_result = [\n                [item[0], escape_html(item[1][0]), item[1][1]]\n                for item in ocr_result\n                if len(item) == 2 and isinstance(item[1], tuple)\n            ]\n\n        try:\n            wired_table_results = self.wired_table_model(np_img, ocr_result)\n            wired_structure_results = (\n                self.wired_table_model(np_img, need_ocr=False)\n                if return_metadata\n                else None\n            )\n","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/opendatalab/MinerU/blob/4fe4bde114a23ee5dd637eae99b767f4669bf58c/mineru/model/table/rec/unet_table/main.py#L262-L298","documentation":"The wired-table predict entry point accepts only PIL Images or numpy arrays; anything else (file path string, bytes, torch tensor) is rejected up front because the code immediately does np.asarray / cv2.cvtColor on it.","triggerScenarios":"Calling predict(input_img='/path/table.png', ...), passing raw bytes from an upload, or passing a torch.Tensor crop from a GPU pipeline.","commonSituations":"Wiring a web endpoint that forwards the uploaded bytes directly, or chaining with a DL pipeline whose outputs are tensors.","solutions":["Load paths first: Image.open(path) or cv2.imread(path).","Wrap bytes: Image.open(BytesIO(data)).","Convert tensors: img = tensor.cpu().numpy().transpose(1, 2, 0)."],"exampleFix":"# before\nresult = model.predict(img_bytes, ocr_result, html)\n\n# after\nfrom io import BytesIO\nfrom PIL import Image\nresult = model.predict(np.asarray(Image.open(BytesIO(img_bytes)).convert('RGB')), ocr_result, html)","handlingStrategy":"type-guard","validationCode":"def to_ndarray(img):\n    if isinstance(img, Image.Image):\n        return np.asarray(img.convert('RGB'))\n    if isinstance(img, np.ndarray):\n        return img\n    if isinstance(img, (bytes, bytearray)):\n        return np.asarray(Image.open(BytesIO(img)).convert('RGB'))\n    if isinstance(img, (str, Path)):\n        return np.asarray(Image.open(img).convert('RGB'))\n    raise TypeError(type(img))","typeGuard":"def is_supported_input(img) -> bool:\n    return isinstance(img, (Image.Image, np.ndarray))","tryCatchPattern":"try:\n    res = model.predict(img, ocr_result, html)\nexcept ValueError as e:\n    if 'pillow object or a numpy array' in str(e):\n        res = model.predict(to_ndarray(img), ocr_result, html)\n    else:\n        raise","preventionTips":["Normalize all image inputs to np.ndarray at your API boundary.","Reject upload payloads with a strict schema (bytes -> decode once).","Convert torch tensors at the pipeline seam, not deep inside consumers."],"tags":["table-recognition","type-validation","api-contract"],"backgroundTag":null,"analyzedSha":"4fe4bde114a23ee5dd637eae99b767f4669bf58c","analyzedAt":"2026-08-14T21:29:18.456Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}