opendatalab/MinerU · error · ValueError

Unsupported image shape for UnimerSwinImageProcessor: {image

Error message

Unsupported image shape for UnimerSwinImageProcessor: {image.shape}

What it means

UnimerSwinImageProcessor.to_normalized_gray_tensor converts arbitrary input arrays to single-channel grayscale before normalization. It accepts 2-D arrays, 3-D with 1 channel, and 3-D with 3 channels; anything else (4-channel RGBA, 2-channel, ndim 1 or 4) is rejected because no defined grayscale conversion exists.

Source

Thrown at mineru/model/mfr/unimernet/unimernet_hf/unimer_swin/image_processing_unimer_swin.py:33

        ):
        self.input_size = [int(_) for _ in image_size]
        assert len(self.input_size) == 2

    def __call__(self, item):
        image = self.prepare_input(item)
        return self.to_normalized_gray_tensor(image)

    @staticmethod
    def to_normalized_gray_tensor(image: np.ndarray) -> torch.Tensor:
        """将图像确定性转灰度、按 UniMERNet 参数归一化,并转为单通道 tensor。"""
        if image.ndim == 2:
            gray = image
        elif image.ndim == 3 and image.shape[2] == 1:
            gray = image[:, :, 0]
        elif image.ndim == 3 and image.shape[2] == 3:
            gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        else:
            raise ValueError(f"Unsupported image shape for UnimerSwinImageProcessor: {image.shape}")

        normalized = (gray.astype(np.float32) - 0.7931 * 255.0) / (0.1738 * 255.0)
        return torch.from_numpy(normalized[None, :, :])

    @staticmethod
    def crop_margin(img: Image.Image) -> Image.Image:
        data = np.array(img.convert("L"))
        data = data.astype(np.uint8)
        max_val = data.max()
        min_val = data.min()
        if max_val == min_val:
            return img
        data = (data - min_val) / (max_val - min_val) * 255
        gray = 255 * (data < 200).astype(np.uint8)

        coords = cv2.findNonZero(gray)  # Find all non-zero points (text)
        a, b, w, h = cv2.boundingRect(coords)  # Find minimum spanning bounding box
        return img.crop((a, b, w + a, h + b))

View on GitHub (pinned to 4fe4bde114)

Solutions

  1. Convert to RGB before calling: img = img.convert('RGB') for PIL or cv2.cvtColor(img, cv2.COLOR_RGBA2BGR) then back, dropping alpha.
  2. If you have a PIL Image, pass modes 'L' or 'RGB' only.
  3. For batched arrays, iterate and pass one (H, W[, C]) image at a time.
  4. Composite RGBA onto a white background if alpha carries meaning.

Example fix

# before
crop = pil_page.crop(box)  # RGBA page render
feat = processor(crop)

# after
crop = pil_page.crop(box).convert('RGB')
feat = processor(crop)
Defensive patterns

Strategy: type-guard

Validate before calling

def normalize_shape(img: np.ndarray) -> np.ndarray:
    if img.ndim == 3 and img.shape[2] not in (1, 3):
        img = img[:, :, :3]
    return img

Type guard

def is_supported_gray_input(img: np.ndarray) -> bool:
    return img.ndim == 2 or (img.ndim == 3 and img.shape[2] in (1, 3))

Try / catch

try:
    feat = processor(crop)
except ValueError as e:
    if 'Unsupported image shape' in str(e):
        feat = processor(np.asarray(Image.fromarray(crop).convert('RGB')))
    else:
        raise

Prevention

When it happens

Trigger: Passing an RGBA crop (shape (H, W, 4)) straight from PIL's .convert('RGBA') or a PNG with alpha into the processor; passing a (H, W, 2) LA-mode array; passing a flattened or batched array with ndim != 2/3.

Common situations: Cropping formula regions from PDF page renders that keep an alpha channel, mixing BGR/RGBA pipelines from OpenCV-based upstream stages, or feeding batched tensors (N, H, W) instead of a single image.

Related errors


AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14). Data as JSON: /api/errors/bcf577f56a1f994e. Report an issue: GitHub.