keras-team/keras · error · ValueError

Input arrays must be multi-channel 2D images.

Error message

Input arrays must be multi-channel 2D images.

What it means

apply_affine_transform works only on 3D arrays (a 2D image plus a channel dimension). If x.ndim != 3 it raises this ValueError right after the axis checks. Grayscale images stored as 2D arrays and batched 4D tensors are both rejected.

Solutions

  1. Expand grayscale images to (H, W, 1) with np.expand_dims(img, -1)
  2. For batches, loop: out[i] = apply_affine_transform(x[i], ...)
  3. Prefer tf.keras.layers.RandomRotation and other preprocessing layers for batched data

Example fix

# before
random_rotation(gray_img, 20)  # shape (H, W)
# after
random_rotation(np.expand_dims(gray_img, -1), 20)  # (H, W, 1)
Defensive patterns

Strategy: validation

Validate before calling

assert img.ndim == 3, f'expected 3D image, got {img.ndim}D'
if img.ndim == 2:
    img = img[..., None]

Type guard

def is_single_image(x):
    return getattr(x, 'ndim', None) == 3

Prevention

When it happens

Trigger: Passing a 2D grayscale image (H, W) with no channel axis; passing a 4D batch (N, H, W, C) directly to apply_affine_transform or random_rotation/random_shift/random_shear/random_zoom.

Common situations: Loading grayscale images with PIL/imageio that yield shape (H, W); forgetting to slice a batch tensor before augmenting one image at a time.

Related errors


AI-assisted analysis of keras-team/keras@7a34a03db6 (2026-08-25). Data as JSON: /api/errors/46d67839ec761ade. Report an issue: GitHub.

Appendix: source

Thrown at keras/src/legacy/preprocessing/image.py:1808

    """
    # Input sanity checks:
    # 1. x must 2D image with one or more channels (i.e., a 3D tensor)
    # 2. channels must be either first or last dimension
    if np.unique([row_axis, col_axis, channel_axis]).size != 3:
        raise ValueError(
            "'row_axis', 'col_axis', and 'channel_axis' must be distinct"
        )

    # shall we support negative indices?
    valid_indices = set([0, 1, 2])
    actual_indices = set([row_axis, col_axis, channel_axis])
    if actual_indices != valid_indices:
        raise ValueError(
            f"Invalid axis' indices: {actual_indices - valid_indices}"
        )

    if x.ndim != 3:
        raise ValueError("Input arrays must be multi-channel 2D images.")
    if channel_axis not in [0, 2]:
        raise ValueError(
            "Channels are allowed and the first and last dimensions."
        )

    transform_matrix = None
    if theta != 0:
        theta = np.deg2rad(theta)
        rotation_matrix = np.array(
            [
                [np.cos(theta), -np.sin(theta), 0],
                [np.sin(theta), np.cos(theta), 0],
                [0, 0, 1],
            ]
        )
        transform_matrix = rotation_matrix

    if tx != 0 or ty != 0:

View on GitHub (pinned to 7a34a03db6)