affaan-m/ECC · error · ValueError

expected (N, 3) pixels, got

Error message

expected (N, 3) pixels, got {pixels.shape}

What it means

`analyze_pixels()` computes grade statistics from a pooled pixel matrix and validates its shape, raising a ValueError unless the array is exactly (N, 3) — N pixels, 3 color channels (RGB). This is the masked-pixel path where callers pool surviving pixels from references of differing sizes, so a wrong reshape or extra channel trips this guard before Lab conversion. Grain estimation gets separate 2-D frames via `noise_frames`.

Solutions

  1. Reshape/convert input to (N, 3): `pixels = img.reshape(-1, 3)` for a single HxWx3 frame.
  2. Strip alpha: `pixels = rgba[..., :3].reshape(-1, 3)` before pooling.
  3. Convert grayscale to RGB with `np.stack([g, g, g], axis=-1)` before pooling.
  4. Assert `pixels.ndim == 2 and pixels.shape[1] == 3` in the caller before invoking.

Example fix

// before
stats = analyze_pixels(frame)                       # frame is (H, W, 3)
// after
pixels = frame.astype(np.float32) / 255.0
stats = analyze_pixels(pixels.reshape(-1, 3))
Defensive patterns

Strategy: type-guard

Validate before calling

pixels = np.asarray(pixels)
if pixels.ndim != 2 or pixels.shape[1] != 3:
    pixels = pixels.reshape(-1, 3)   # or convert RGBA -> RGB first

Type guard

def is_pixel_matrix(a) -> bool:
    import numpy as np
    a = np.asarray(a)
    return a.ndim == 2 and a.shape[1] == 3

Try / catch

try:
    stats = analyze_pixels(pixels, ...)
except ValueError as e:
    if 'expected (N, 3)' in str(e):
        stats = analyze_pixels(pixels.reshape(-1, 3), ...)
    else:
        raise

Prevention

When it happens

Trigger: Calling `analyze_pixels(pixels, ...)` with a 1-D array, a (H, W, 3) image instead of pooled pixels, an (N, 4) RGBA array, or an (N, 1) single-channel array.

Common situations: Forgetting `.reshape(-1, 3)` on a per-frame image; passing raw uint8 RGBA masks instead of RGB; stacking frames with an alpha channel; mixing grayscale frames into the pool.

Understand the failure class

Background: Tensor shape mismatch errors ("must have shape", "expected shape ... got ..."): when tensor dimensions disagree with what an op or layer was told to expect — this error's family across 6 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/a1f84f048c97275d. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/taste/grade.py:615

def load_stats(path: str | Path) -> GradeStats:
    return GradeStats.from_dict(json.loads(Path(path).read_text(encoding="utf-8")))


def analyze_pixels(
    pixels: np.ndarray,
    noise_frames: list[np.ndarray] | None = None,
    palette_pixels: np.ndarray | None = None,
) -> GradeStats:
    """Same statistics as :func:`analyze`, but from a flat (N, 3) pixel array.

    This is the masked path: callers pool only the pixels that survived
    content masking, across references of differing frame sizes, and pass
    them here. Grain still needs 2-D neighbourhoods, so ``noise_frames``
    carries a handful of cropped frames purely for that estimate.
    """
    if pixels.ndim != 2 or pixels.shape[1] != 3:
        raise ValueError(f"expected (N, 3) pixels, got {pixels.shape}")

    lab = _to_lab(np.ascontiguousarray(pixels.reshape(1, -1, 3), np.float32)).reshape(-1, 3)
    L, a, b = lab[:, 0], lab[:, 1], lab[:, 2]
    chroma = np.sqrt(a.astype(np.float64) ** 2 + b.astype(np.float64) ** 2)

    pal_src = palette_pixels if palette_pixels is not None else pixels
    pal = _palette([pal_src.reshape(1, -1, 3)])

    return GradeStats(
        zones=_zone_stats(L, a, b),
        lab_mean=[float(L.mean()), float(a.mean()), float(b.mean())],
        lab_std=[float(L.std()), float(a.std()), float(b.std())],
        l_cdf=[float(v) for v in _cdf_of_l(L)],
        black_point=float(np.percentile(L, 1)),
        white_point=float(np.percentile(L, 99)),
        contrast=float(L.std()),
        saturation=float(chroma.mean()),
        warmth=float(b.mean()),

View on GitHub (pinned to 8321021c54)