# How Resolution Scaling with `_scale_filter` Ensures Claude Read Compatibility (Max 512px Wide, 1998px Tall)

> Learn how _scale_filter in bradautomates/claude-video ensures Claude Read compatibility by using ffmpeg to scale images, maintaining aspect ratio within max dimensions.

- Repository: [bradautomates/claude-video](https://github.com/bradautomates/claude-video)
- Tags: deep-dive
- Published: 2026-08-10

---

**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`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py). Here's the implementation:

```python
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)` where `MAX_READ_DIMENSION = 1998` enforces Claude's absolute height limit
- **Aspect ratio preservation** — `force_original_aspect_ratio=decrease` ensures the image shrinks to fit within the bounding box without distortion or cropping
- **Codec compatibility** — `force_divisible_by=2` rounds 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`](https://github.com/bradautomates/claude-video/blob/main/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:

```python
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:

```python
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:

```python
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:

```python
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)

```bash
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

```bash
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

```python
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()` in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) guarantees all extracted frames respect Claude's dimension limits
- **Height ceiling**: The 1998px maximum is hardcoded via `MAX_READ_DIMENSION` and applied via `min(1998,ih)`
- **Width configurability**: Default 512px resolution can be overridden while maintaining height safety
- **Aspect ratio integrity**: `force_original_aspect_ratio=decrease` prevents 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`](https://github.com/bradautomates/claude-video/blob/main/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.