roboflow/supervision · error · ValueError

Panoptic PNG masks must have at least 3 channels.

Error message

Panoptic PNG masks must have at least 3 channels.

What it means

Raised when decoding a panoptic-segmentation PNG whose decoded array has 3 dimensions but fewer than 3 channels in the last axis. Panoptic IDs are RGB-encoded as little-endian 24-bit integers (R + G<<8 + B<<16), so at least R, G, and B channels must exist; a 1- or 2-channel PNG (grayscale with an extra axis, or LA alpha-only layouts) cannot carry the encoding. Pure 2-D grayscale masks are handled earlier and returned as-is.

Source

Thrown at src/supervision/detection/tools/transformers.py:229

def png_string_to_segmentation_array(png_string: bytes) -> npt.NDArray[Any]:
    """
    Convert a PNG byte string to a panoptic segmentation array.

    Args:
        png_string: A byte string representing the PNG image.

    Returns:
        A segmentation ID array with shape (H, W), where each unique value
            represents a different object or category. RGB-encoded panoptic
            PNGs are decoded as little-endian 24-bit integers; alpha is ignored.
    """
    image = Image.open(io.BytesIO(png_string))
    mask = np.array(image, dtype=np.uint8)
    if mask.ndim == 2:
        return mask.astype(np.uint32)
    if mask.shape[2] < 3:
        raise ValueError("Panoptic PNG masks must have at least 3 channels.")

    segmentation = (
        mask[:, :, 0].astype(np.uint32)
        + (mask[:, :, 1].astype(np.uint32) << 8)
        + (mask[:, :, 2].astype(np.uint32) << 16)
    )
    return cast(npt.NDArray[Any], segmentation)


def append_class_names_to_data(
    class_ids: npt.NDArray[Any],
    id2label: dict[int, str] | None,
    data: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """
    Helper function to create or append to a data dictionary with class names if
    available.

View on GitHub (pinned to 7f254d9784)

Solutions

  1. Regenerate or re-save the PNG in RGB mode: Image.open(p).convert('RGB').save(p) before passing the bytes.
  2. If the mask is genuinely class-ID grayscale, pass a 2-D single-channel PNG so it takes the grayscale path instead.
  3. Check the producer of png_string — it should emit the original model panoptic PNG unmodified.

Example fix

# before
png_bytes = open('panoptic_la.png', 'rb').read()
segmentation = decode_png_string(png_bytes)  # ValueError

# after
from PIL import Image
img = Image.open('panoptic_la.png').convert('RGB')
import io
buf = io.BytesIO(); img.save(buf, format='PNG')
segmentation = decode_png_string(buf.getvalue())
Defensive patterns

Strategy: validation

Validate before calling

import io
import numpy as np
from PIL import Image

def load_panoptic_png(png_bytes: bytes) -> np.ndarray:
    img = Image.open(io.BytesIO(png_bytes))
    if img.mode not in ('RGB', 'L'):
        img = img.convert('RGB')
    return np.array(img)

segmentation = decode_png_string(png_bytes) if is_valid_panoptic_png(png_bytes) else ...

Type guard

def is_valid_panoptic_png(png_bytes: bytes) -> bool:
    import io
    from PIL import Image
    arr = np.array(Image.open(io.BytesIO(png_bytes)))
    return arr.ndim == 2 or arr.shape[2] >= 3

Try / catch

try:
    segmentation = decode_png_string(png_bytes)
except ValueError as err:
    if 'at least 3 channels' in str(err):
        img = Image.open(io.BytesIO(png_bytes)).convert('RGB')
        buf = io.BytesIO(); img.save(buf, format='PNG')
        segmentation = decode_png_string(buf.getvalue())
    else:
        raise

Prevention

When it happens

Trigger: Calling the panoptic-PNG decoder (used by Transformers connectors, e.g. for Mask2Former/MaskFormer post-processing) with an LA-mode (luminance+alpha) PNG or another 2-channel image saved by an upstream tool.

Common situations: A model or preprocessing step converts the panoptic PNG to grayscale-with-alpha before it reaches supervision; corrupted or re-encoded PNG files from a dataset pipeline; version changes in an upstream library that alters the saved PNG mode.

Related errors


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/c176652e331316d5. Report an issue: GitHub.