opendatalab/MinerU · error · ValueError
Input must be a pillow object or a numpy array.
Error message
Input must be a pillow object or a numpy array.
What it means
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.
Source
Thrown at mineru/model/table/rec/unet_table/main.py:280
td_count = html_lower.count('<td')
th_count = html_lower.count('<th')
return td_count + th_count
class UnetTableModel:
def __init__(self, ocr_engine):
model_path = os.path.join(auto_download_and_get_model_root_path(ModelPath.unet_structure), ModelPath.unet_structure)
wired_input_args = WiredTableInput(model_path=model_path)
self.wired_table_model = WiredTableRecognition(wired_input_args, ocr_engine)
self.ocr_engine = ocr_engine
def predict(self, input_img, ocr_result, wireless_html_code, return_metadata: bool = False):
if isinstance(input_img, Image.Image):
np_img = np.asarray(input_img)
elif isinstance(input_img, np.ndarray):
np_img = input_img
else:
raise ValueError("Input must be a pillow object or a numpy array.")
bgr_img = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)
if ocr_result is None:
ocr_result = self.ocr_engine.ocr(bgr_img)[0]
ocr_result = [
[item[0], escape_html(item[1][0]), item[1][1]]
for item in ocr_result
if len(item) == 2 and isinstance(item[1], tuple)
]
try:
wired_table_results = self.wired_table_model(np_img, ocr_result)
wired_structure_results = (
self.wired_table_model(np_img, need_ocr=False)
if return_metadata
else None
)
View on GitHub (pinned to 4fe4bde114)
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).
Example fix
# before
result = model.predict(img_bytes, ocr_result, html)
# after
from io import BytesIO
from PIL import Image
result = model.predict(np.asarray(Image.open(BytesIO(img_bytes)).convert('RGB')), ocr_result, html) Defensive patterns
Strategy: type-guard
Validate before calling
def to_ndarray(img):
if isinstance(img, Image.Image):
return np.asarray(img.convert('RGB'))
if isinstance(img, np.ndarray):
return img
if isinstance(img, (bytes, bytearray)):
return np.asarray(Image.open(BytesIO(img)).convert('RGB'))
if isinstance(img, (str, Path)):
return np.asarray(Image.open(img).convert('RGB'))
raise TypeError(type(img)) Type guard
def is_supported_input(img) -> bool:
return isinstance(img, (Image.Image, np.ndarray)) Try / catch
try:
res = model.predict(img, ocr_result, html)
except ValueError as e:
if 'pillow object or a numpy array' in str(e):
res = model.predict(to_ndarray(img), ocr_result, html)
else:
raise Prevention
- 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.
When it happens
Trigger: Calling predict(input_img='/path/table.png', ...), passing raw bytes from an upload, or passing a torch.Tensor crop from a GPU pipeline.
Common situations: Wiring a web endpoint that forwards the uploaded bytes directly, or chaining with a DL pipeline whose outputs are tensors.
Related errors
- The img type {type(img)} does not in {InputType.__args__}
- Scale must be a number or tuple of int, but got {type(scale)
- images_mfd_res and images must have the same length.
- Input image ({w}, {h}) smaller than the target size ({cw}, {
- {model_path} does not exists.
AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14).
Data as JSON: /api/errors/d2ba4b2cf2e536b1.
Report an issue: GitHub.