affaan-m/ECC · error · ValueError

cannot read plate

Error message

cannot read plate: {path}

What it means

plates.py raises ValueError('cannot read plate: {path}') when OpenCV's cv2.imread returns None, meaning the image file could not be loaded or decoded. The library expects an image file with an alpha channel (RGBA) or opaque content to crop/scale. This guards downstream code from operating on an empty array.

Solutions

  1. Verify the file exists at the exact path and print the resolved absolute path (Path(path).resolve()) before loading.
  2. Open the image with PIL to confirm it is decodable; if PIL reads it but cv2 does not, re-save/re-encode it as PNG.
  3. Fix the working-directory-relative path or pass an absolute path.
  4. Check file permissions and that the path is not a directory.

Example fix

// before
cv2.imread('assets/plate.png')  # silently None if cwd differs
// after
from pathlib import Path
p = Path('assets/plate.png').resolve()
assert p.is_file(), f'missing plate: {p}'
im = cv2.imread(str(p), cv2.IMREAD_UNCHANGED)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
import cv2
def validate_plate(path):
    p = Path(path)
    if not p.is_file():
        raise FileNotFoundError(path)
    im = cv2.imread(str(p), cv2.IMREAD_UNCHANGED)
    if im is None:
        raise ValueError(f'undecodable image: {p}')
    return p

Type guard

def is_readable_image(path) -> bool:
    from pathlib import Path
    import cv2
    p = Path(path)
    return p.is_file() and cv2.imread(str(p), cv2.IMREAD_UNCHANGED) is not None

Try / catch

try:
    tighten(plate_path)
except ValueError as e:
    if 'cannot read plate' in str(e):
        logger.error('plate unreadable: %s', plate_path)
        plate_path = fallback_plate

Prevention

When it happens

Trigger: Calling the plate-loading function (RAISED IN tighten) with a path that does not exist, is a directory, has an unsupported/corrupt image format, or a non-ASCII/permission-blocked path so cv2.imread returns None.

Common situations: Typo in file path; file was deleted or moved after being referenced in config; image saved without an extension OpenCV recognizes; relative path resolved from the wrong working directory; corrupted download.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/taste/plates.py:225

def tighten(path: str | Path, dest: str | Path | None = None, pad: float = 0.06) -> Path:
    """Crop a plate to its own content, so the element fills the file.

    A glow plate is mostly empty by construction - the selection keeps the top
    few percent of pixels by energy, so a typical plate is 2-7% covered and
    97% transparent black. Compositing that at full frame produces a small
    bright dot floating in the middle of the shot, which reads as a sticker
    rather than as light. Measured on the first cut: a plate covering 1.7% of
    its own frame, screen-blended full-frame, was visible only as a coloured
    blob near centre.

    Cropping to the alpha bounding box means the caller controls the element's
    size on screen by scaling, instead of inheriting whatever fraction of the
    source frame the element happened to occupy.
    """
    path = Path(path)
    im = cv2.imread(str(path), cv2.IMREAD_UNCHANGED)
    if im is None:
        raise ValueError(f"cannot read plate: {path}")
    alpha = im[..., 3] if im.shape[2] == 4 else im[..., :3].max(axis=2)
    ys, xs = np.where(alpha > 12)
    if len(ys) == 0:
        return path
    h, w = alpha.shape
    py, px = int(h * pad), int(w * pad)
    y0 = max(0, int(ys.min()) - py); y1 = min(h, int(ys.max()) + py + 1)
    x0 = max(0, int(xs.min()) - px); x1 = min(w, int(xs.max()) + px + 1)
    out = Path(dest) if dest else path.with_name(path.stem + "_tight.png")
    out.parent.mkdir(parents=True, exist_ok=True)
    cv2.imwrite(str(out), im[y0:y1, x0:x1])
    return out


def plate_coverage(path: str | Path) -> float:
    """Share of the plate that is actually lit. Drives element-vs-wash choice."""
    im = cv2.imread(str(path), cv2.IMREAD_UNCHANGED)
    if im is None:

View on GitHub (pinned to 8321021c54)