# When Does the Frame Cap Apply in claude-video After Deduplication?

> Discover when the frame cap applies in claude-video after deduplication. Learn how unique content is prioritized to optimize your video processing budget.

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

---

**The frame cap (`max_frames`) is enforced only after the deduplication process removes visually similar frames, ensuring the final budget is spent on unique content rather than duplicates.**

The `bradautomates/claude-video` repository provides a robust frame extraction pipeline for video processing. Understanding exactly when the **frame cap** applies is critical for optimizing your video analysis budget and ensuring you capture the most diverse visual content. According to the source code in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py), the cap is imposed as a final filtering step, not during the initial candidate generation.

## Frame Cap Timing in the Extraction Pipeline

The extraction logic follows a strict three-phase sequence to maximize content diversity while respecting user-defined limits. The **frame cap** acts as the final gatekeeper, operating only on the deduplicated set.

### Step 1: Candidate Extraction

First, the engine gathers all potential frames using methods like `extract_scene_candidates()` or `extract_keyframes()`. This phase returns an unfiltered list of every scene change, key frame, or uniform sample detected in the video, often numbering in the hundreds regardless of your target budget.

### Step 2: Perceptual Deduplication

Next, the system runs `dedupe_perceptual()` (or the internal helper `_dedupe_by_deltas`) to eliminate near-identical frames. This step compares visual hashes or frame deltas to drop redundant content, preventing wasted tokens on duplicate visual information.

### Step 3: Cap Enforcement via Even Sampling

Finally, the surviving frames are passed to `_even_sample()`, which trims the list down to the `max_frames` limit. This is where the **frame cap** is actually applied. As implemented in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) lines 44–50, the logic first deduplicates, then calculates the cap, then samples:

```python

# From extract_scene_or_uniform (lines 44-50)

deduped, n_dropped = dedupe_perceptual(scene_frames) if dedup else (scene_frames, 0)
cap = len(deduped) if max_frames is None else max_frames
selected = _even_sample(deduped, cap)

```

The same pattern appears in the key-frame engine at lines 70–75, confirming this is the canonical behavior across extraction modes.

## Code Implementation in frames.py

The [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) file contains the core logic that dictates this order. The critical insight is that `max_frames` is evaluated against the `deduped` list length, not the original candidate list.

In the scene-based extraction path:

```python
deduped, n_dropped = dedupe_perceptual(scene_frames) if dedup else (scene_frames, 0)
cap = len(deduped) if max_frames is None else max_frames
selected = _even_sample(deduped, cap)

```

In the key-frame extraction path:

```python
deduped, n_dropped = dedupe_perceptual(candidates) if dedup else (candidates, 0)
cap = len(deduped) if max_frames is None else max_frames
selected = _even_sample(deduped, cap)

```

Both paths demonstrate that **deduplication always precedes the cap**. If `max_frames` is set to `None`, the cap defaults to the length of the deduplicated list, effectively returning all unique frames.

## Practical Examples

### Using Scene or Uniform Extraction

When calling `extract_scene_or_uniform`, the `max_frames` parameter acts as a post-dedup limit:

```python
from pathlib import Path
from skills.watch.scripts.frames import extract_scene_or_uniform

video_path = "sample.mp4"
out_dir = Path("./frames")

# Request 100 frames, but only after removing duplicates

frames, meta = extract_scene_or_uniform(
    video_path,
    out_dir,
    fps=2.0,
    target_frames=100,
    max_frames=100,
    dedup=True,
)

print(f"Returned {len(frames)} frames (cap applied after dedup)")
print(meta)

```

Even if the video contains 300 scene changes, the engine first removes visual duplicates, then selects an evenly distributed sample of 100 from the remaining unique frames.

### Using Key-Frame Extraction

The same behavior applies to `extract_keyframes`:

```python
from pathlib import Path
from skills.watch.scripts.frames import extract_keyframes

frames, meta = extract_keyframes(
    "sample.mp4",
    Path("./keyframes"),
    max_frames=20,
    dedup=True,
)

print(f"Key-frames after dedup and capping: {len(frames)}")

```

If the video yields 150 distinct key-frames before deduplication, but only 80 remain after removing near-duplicates, the final cap of 20 is applied to those 80, resulting in 20 evenly spaced frames.

## Why This Order Matters

Applying the **frame cap after deduplication** ensures that your `max_frames` budget is never consumed by redundant visual data. If the cap were applied first, you risk retaining multiple near-identical frames while discarding unique content from later in the video. By deduplicating first, `claude-video` guarantees that every frame in the final output represents distinct visual information, with `_even_sample()` providing temporal coverage across the entire deduplicated set.

## Summary

- The **frame cap** (`max_frames`) is enforced in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) only after the `dedupe_perceptual()` step completes.
- Initial candidate extraction generates an uncapped list of potential frames.
- **Deduplication** removes visual duplicates before the budget constraint is applied.
- The `_even_sample()` function performs the final trimming to meet the `max_frames` limit.
- This sequence ensures optimal use of your frame budget on unique visual content.

## Frequently Asked Questions

### Does the frame cap apply before or after deduplication?

The frame cap applies **after** deduplication. According to the source code in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py), the `dedupe_perceptual()` function runs first, and its output is then passed to `_even_sample()` along with the `max_frames` value. This ensures that only unique frames are counted toward your budget.

### What happens if there are fewer frames than max_frames after deduplication?

If the deduplication process yields fewer frames than the specified `max_frames` value, the `_even_sample()` function returns all remaining frames without error. The cap acts as a ceiling, not a requirement, so you will never receive more frames than exist in the deduplicated set.

### Which function handles the final frame sampling?

The `_even_sample()` function in [`skills/watch/scripts/frames.py`](https://github.com/bradautomates/claude-video/blob/main/skills/watch/scripts/frames.py) handles the final sampling. It receives the deduplicated frame list and the calculated cap (either `max_frames` or the length of the deduplicated list), then returns an evenly distributed subset that respects the limit.

### Can I apply the frame cap without deduplicating frames?

Yes. You can disable deduplication by setting `dedup=False` in functions like `extract_scene_or_uniform` or `extract_keyframes`. When deduplication is disabled, the frame cap is applied directly to the raw candidate list via `_even_sample()`, meaning duplicates may be included in your final frame count if they exist in the initial extraction.