affaan-m/ECC · error · ValueError
Crop must fit within normalized image coordinates
Error message
Crop must fit within normalized image coordinates
What it means
TasteForge validates that every crop rectangle for a still-image zoom clip consists of exactly four finite numbers and lies fully inside the normalized [0,1] image coordinate space (x,y >= 0, w,h > 0, x+w <= 1, y+h <= 1). filter_graph raises this error before building the ffmpeg filter graph when a start or end crop extends beyond the image. This prevents zoompan from producing out-of-bounds sampling.
Solutions
- Normalize the crop: clamp x,y to >= 0 and shrink w,h so x+w <= 1 and y+h > 0, e.g. [0.2, 0.2, 0.5, 0.5]
- Convert pixel coordinates to fractions: divide x and w by image width, y and h by image height before calling make_clip
- Add an assertion/clamp helper in your pipeline that keeps crop rectangles within the unit square
Example fix
// before
make_clip("in.jpg", "out.mp4", start_crop=[0.4, 0.4, 0.7, 0.7], end_crop=[0.1, 0.1, 0.5, 0.5]) # 0.4+0.7 > 1
// after
make_clip("in.jpg", "out.mp4", start_crop=[0.2, 0.2, 0.5, 0.5], end_crop=[0.1, 0.1, 0.5, 0.5]) Defensive patterns
Strategy: validation
Validate before calling
def valid_crop(c):
return (isinstance(c, (list, tuple)) and len(c) == 4
and all(isinstance(v, (int, float)) and math.isfinite(v) for v in c)
and c[0] >= 0 and c[1] >= 0 and c[2] > 0 and c[3] > 0
and c[0] + c[2] <= 1 and c[1] + c[3] <= 1)
assert valid_crop(start_crop) and valid_crop(end_crop) Prevention
- Keep crop values as fractions of image dimensions, never pixels
- Clamp with min(x, 1 - w) style logic before use
- Add a unit test asserting x+w <= 1 and y+h <= 1 for all crop presets
When it happens
Trigger: Calling make_clip with a start_crop or end_crop where x<0, y<0, w<=0, h<=0, x+w>1, or y+h>1 (e.g. [0.1, 0.1, 0.9, 0.9] summing to 1.0 exactly is fine, but [0.5, 0.5, 0.6, 0.6] exceeds 1). Also raised when a crop value is non-finite (inf/nan) — though that yields the sibling 'four finite values' error first.
Common situations: Hand-computing crop rectangles in pixel units instead of normalized 0-1 fractions; forgetting that width and height add to x and y; deriving crops from aspect-ratio math that rounds to slightly over 1.0; passing NaN from an upstream computation.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Geometry, fps and duration must be finite and positive
- nothing to concatenate
- nothing to concatenate
- -32602
- a claim token is required
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/2621632acf2929a9.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/media/stills.py:17
"""Animate an image between normalized crop rectangles using local FFmpeg."""
import argparse
import math
import subprocess
from pathlib import Path
from .common import geometry, output_file
def filter_graph(start, end, frames, width, height, fps):
for crop in (start, end):
if len(crop) != 4 or any(not math.isfinite(v) for v in crop):
raise ValueError("Crop must contain four finite values")
x, y, w, h = crop
if min(x, y) < 0 or min(w, h) <= 0 or x + w > 1 or y + h > 1:
raise ValueError("Crop must fit within normalized image coordinates")
# Coordinates refer to the image after scale-cover to output aspect.
# Expand each requested rectangle to output aspect, then interpolate its
# width and center; zoompan clamps the window at image boundaries.
fraction = f"on/{max(1, frames - 1)}"
def interpolate(a, b):
return f"({a}+({b}-{a})*{fraction})"
zoom = f"1/{interpolate(max(start[2], start[3]), max(end[2], end[3]))}"
cx = interpolate(start[0] + start[2] / 2, end[0] + end[2] / 2)
cy = interpolate(start[1] + start[3] / 2, end[1] + end[3] / 2)
return (
f"scale={width * 2}:{height * 2}:force_original_aspect_ratio=increase,"
f"crop={width * 2}:{height * 2},zoompan=z='{zoom}':"
f"x='iw*{cx}-iw/zoom/2':y='ih*{cy}-ih/zoom/2':"
f"d={frames}:s={width}x{height}:fps={fps},format=yuv420p"
)
View on GitHub (pinned to 8321021c54)