How Resolution Scaling with `_scale_filter` Ensures Claude Read Compatibility (Max 512px Wide, 1998px Tall)
The _scale_filter helper in bradautomates/claude-video enforces hard limits on extracted frame dimensions using ffmpeg's scale filter with min() expressions, guaranteeing images never exceed 1998px in height or the configured width while preserving aspect ratio.
The claude-video project provides a robust video analysis toolkit designed specifically for Claude's vision capabilities. One critical constraint when sending images to Claude is the Read model's maximum dimension limits: frames must be no wider than the configured resolution (default 512px) and absolutely no taller than 1998px. The project handles this automatically through a centralized scaling filter that every frame extraction pipeline uses.
How _scale_filter Builds the FFmpeg Scale Expression
The entire safety mechanism lives in a single private function in skills/watch/scripts/frames.py. Here's the implementation:
def _scale_filter(resolution: int) -> str:
return (
f"scale=w='min({resolution},iw)':h='min({MAX_READ_DIMENSION},ih)':"
"force_original_aspect_ratio=decrease:force_divisible_by=2"
)
This function generates an ffmpeg video filter (-vf) string that combines four protective constraints:
- Width clamping —
min({resolution},iw)selects the smaller of the user-requested width or the source width, preventing upscaling beyond the original - Height ceiling —
min({MAX_READ_DIMENSION},ih)whereMAX_READ_DIMENSION = 1998enforces Claude's absolute height limit - Aspect ratio preservation —
force_original_aspect_ratio=decreaseensures the image shrinks to fit within the bounding box without distortion or cropping - Codec compatibility —
force_divisible_by=2rounds dimensions to even integers, satisfying both ffmpeg encoder requirements and Claude's image pipeline
Where the Scale Filter Is Applied
Every frame extraction path in skills/watch/scripts/frames.py incorporates _scale_filter(resolution) to maintain consistent safety guarantees:
Uniform Frame Extraction (extract function)
Line 94-96 applies the filter after fps-based frame selection:
cmd = (
f"ffmpeg -i {shlex.quote(str(video_file))} "
f"-vf fps={fps},{_scale_filter(resolution)} " # ← scaling applied here
f"-q:v {QUALITY} {shlex.quote(str(output_dir))}/frame_%06d.jpg"
)
Scene-Change Detection (extract_scene_candidates function)
Line 52-56 chains the scale filter after scene selection:
vf = f"select='eq(n\\,0)+gt(scene\\,{threshold})',{_scale_filter(resolution)},showinfo"
Keyframe Extraction (extract_keyframes function)
Line 14-16 applies scaling to all keyframes:
cmd = (
f"ffmpeg -i {shlex.quote(str(video_file))} "
f"-vf {_scale_filter(resolution)},showinfo " # direct scaling
f"-vsync 0 -q:v {QUALITY} {shlex.quote(str(output_dir))}/keyframe_%06d.jpg"
)
Timestamp-Based Extraction (extract_at_timestamps function)
Line 70-73 uses the filter for cue frame generation:
cmd = (
f"ffmpeg -ss {timestamp} -i {shlex.quote(str(video_file))} "
f"-vf {_scale_filter(resolution)} " # scaling at specific times
f"-vframes 1 -q:v {QUALITY} {shlex.quote(str(output_path))}"
)
Practical Usage Examples
Default Safe Extraction (512px width, ≤1998px height)
python -m skills.watch.scripts.frames extract video.mp4 ./out --fps 1.5
Internal ffmpeg command includes:
-vf "fps=1.5,scale=w='min(512,iw)':h='min(1998,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2"
Higher Resolution with Height Protection
python -m skills.watch.scripts.frames extract video.mp4 ./out \
--resolution 1024
Width may reach 1024px, but height remains capped at 1998px regardless of source aspect ratio.
Direct Filter Usage in Custom Pipelines
from skills.watch.scripts.frames import _scale_filter
resolution = 800
filter_str = _scale_filter(resolution)
cmd = [
"ffmpeg", "-i", "video.mp4",
"-vf", filter_str,
"-q:v", "4",
"frame_%04d.jpg"
]
# filter_str evaluates to:
# "scale=w='min(800,iw)':h='min(1998,ih)':force_original_aspect_ratio=decrease:force_divisible_by=2"
Why 1998px? Understanding Claude's Constraints
The MAX_READ_DIMENSION = 1998 constant reflects a documented limitation of Claude's image reading capability. Exceeding this height triggers processing errors or automatic rejection. The _scale_filter design prioritizes height compliance over width—even if a user requests an extremely wide resolution, the height constraint takes precedence through the force_original_aspect_ratio=decrease parameter, which shrinks the entire image to fit within the height limit.
Summary
- Centralized enforcement:
_scale_filter()inskills/watch/scripts/frames.pyguarantees all extracted frames respect Claude's dimension limits - Height ceiling: The 1998px maximum is hardcoded via
MAX_READ_DIMENSIONand applied viamin(1998,ih) - Width configurability: Default 512px resolution can be overridden while maintaining height safety
- Aspect ratio integrity:
force_original_aspect_ratio=decreaseprevents distortion or overflow - Universal coverage: All four extraction methods—uniform, scene-based, keyframe, and timestamp—apply the identical filter
Frequently Asked Questions
What happens if my source video is taller than 1998px?
The _scale_filter automatically scales it down. The min(1998,ih) expression means if the source height (ih) exceeds 1998, the output height becomes 1998 and the width scales proportionally. The frame will be shorter than the original but fully compliant with Claude's requirements.
Can I disable the height limit if I'm not using Claude?
The MAX_READ_DIMENSION constant is hardcoded in skills/watch/scripts/frames.py. To change it, you would need to modify the source code directly. The project is designed specifically for Claude integration, so the 1998px limit is intentionally non-configurable through command-line arguments.
Why does the filter require dimensions divisible by 2?
Many ffmpeg video codecs require even dimensions for proper encoding, and Claude's image processing pipeline has similar constraints. The force_divisible_by=2 parameter ensures compatibility at both the ffmpeg output stage and the downstream consumption stage without adding visible padding or cropping artifacts.
Does increasing --resolution ever violate the height limit?
No. Because force_original_aspect_ratio=decrease is always active, the scaling algorithm treats the width and height constraints as a bounding box rather than target dimensions. A very wide requested resolution combined with a tall source video will result in a height-limited output where the width is smaller than requested to maintain the original aspect ratio.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →