keras-team/keras · error · ValueError

Invalid end_points shape: expected (4,2) for a single image

Error message

Invalid end_points shape: expected (4,2) for a single image or (N,4,2) for a batch. Received shape: {end_points.shape}

What it means

The perspective op requires end_points to mirror start_points: trailing dims (4, 2) with ndim 2 or 3. This fires when the destination corners are malformed even though start_points passed.

Source

Thrown at keras/src/ops/image.py:2189

            interpolation=self.interpolation,
            fill_value=self.fill_value,
            data_format=self.data_format,
        )

    def compute_output_spec(self, images, start_points, end_points):
        if len(images.shape) not in (3, 4):
            raise ValueError(
                "Invalid images rank: expected rank 3 (single image) "
                "or rank 4 (batch of images). Received input with shape: "
                f"images.shape={images.shape}"
            )
        if start_points.shape[-2:] != (4, 2) or start_points.ndim not in (2, 3):
            raise ValueError(
                "Invalid start_points shape: expected (4,2) for a single image"
                f" or (N,4,2) for a batch. Received shape: {start_points.shape}"
            )
        if end_points.shape[-2:] != (4, 2) or end_points.ndim not in (2, 3):
            raise ValueError(
                "Invalid end_points shape: expected (4,2) for a single image"
                f" or (N,4,2) for a batch. Received shape: {end_points.shape}"
            )
        if start_points.shape != end_points.shape:
            raise ValueError(
                "start_points and end_points must have the same shape."
                f" Received start_points.shape={start_points.shape}, "
                f"end_points.shape={end_points.shape}"
            )
        return KerasTensor(images.shape, dtype=images.dtype)


@keras_export("keras.ops.image.perspective_transform")
def perspective_transform(
    images,
    start_points,
    end_points,
    interpolation="bilinear",

View on GitHub (pinned to 7a34a03db6)

Solutions

  1. Apply the same reshape to end_points: (4, 2) or (N, 4, 2).
  2. Derive both arrays from one helper so their layouts cannot diverge.
  3. Add an assert end_points.shape[-2:] == (4, 2) before calling.

Example fix

# before
end_points = dst.reshape(-1)

# after
end_points = dst.reshape(4, 2)
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np
ep = np.asarray(end_points)
if ep.ndim == 1 and ep.size == 8: ep = ep.reshape(4, 2)
assert ep.ndim in (2, 3) and ep.shape[-2:] == (4, 2), ep.shape

Type guard

def valid_corner_points(p):
    p = np.asarray(p)
    return p.ndim in (2, 3) and tuple(p.shape[-2:]) == (4, 2)

Prevention

When it happens

Trigger: perspective(img, start_points (4,2), end_points flat array of 8); end_points shaped (N, 2, 4) for a batch; only one of the two arrays reshaped after refactoring.

Common situations: Computing destination corners with a different code path (e.g. ordering corners via a polygon library) that yields a different layout; partially migrating legacy code.

Related errors


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