Files
claude-video/tests/test_dedup.py
T
bradautomates 304b639b4d Add frame dedup and Whisper auto-chunking
Frame extraction now runs a perceptual dedup pass (default on, --no-dedup
to disable) that drops near-identical frames before the budget cap, so the
budget goes to distinct content instead of held slides/static recordings.
The Frames report line notes how many near-duplicates were dropped.

Whisper transcription splits audio over the 25 MB upload cap into evenly
sized chunks, transcribes each, shifts segment timestamps back into source
time, and tolerates partial failures (only fails if every chunk fails) —
length alone no longer fails transcription.

token-burner is exempt from the long-video sparse-scan warning since it
keeps every scene-change frame. README/SKILL.md updated. Adds test_dedup.py and test_whisper.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 23:16:17 +10:00

163 lines
6.0 KiB
Python

"""Frame-delta dedup: per-pixel difference, greedy de-duplication, integration."""
from __future__ import annotations
from pathlib import Path
import frames
# --- _frame_delta: mean absolute per-pixel difference ------------------------
def test_frame_delta_identical_is_zero():
a = bytes([10] * 16)
assert frames._frame_delta(a, a) == 0.0
def test_frame_delta_is_mean_absolute_difference():
a = bytes([0, 0, 0, 0])
b = bytes([4, 0, 0, 0])
assert frames._frame_delta(a, b) == 1.0 # (4+0+0+0)/4
def test_frame_delta_mismatched_length_is_infinite():
assert frames._frame_delta(bytes([1, 2]), bytes([1, 2, 3])) == float("inf")
# --- _dedupe_by_deltas: greedy drop vs last *kept* thumbnail ------------------
def _touch(dirpath: Path, n: int) -> list[dict]:
dirpath.mkdir(parents=True, exist_ok=True)
out = []
for i in range(n):
p = dirpath / f"frame_{i:04d}.jpg"
p.write_bytes(b"x")
out.append({"index": i, "timestamp_seconds": float(i), "path": str(p), "reason": "scene-change"})
return out
FLAT0 = bytes([0, 0, 0, 0])
FLAT255 = bytes([255, 255, 255, 255])
def test_dedupe_collapses_identical_run(tmp_path: Path):
cands = _touch(tmp_path, 5)
thumbs = [FLAT0, FLAT0, FLAT0, FLAT0, FLAT0]
survivors, dropped = frames._dedupe_by_deltas(cands, thumbs, threshold=2.0)
assert dropped == 4
assert len(survivors) == 1
assert survivors[0]["index"] == 0
assert sorted(p.name for p in tmp_path.glob("frame_*.jpg")) == ["frame_0000.jpg"]
def test_dedupe_keeps_all_distinct(tmp_path: Path):
cands = _touch(tmp_path, 4)
thumbs = [FLAT0, FLAT255, FLAT0, FLAT255]
survivors, dropped = frames._dedupe_by_deltas(cands, thumbs, threshold=2.0)
assert dropped == 0
assert [s["index"] for s in survivors] == [0, 1, 2, 3]
assert len(list(tmp_path.glob("frame_*.jpg"))) == 4
def test_dedupe_compares_against_last_kept_not_previous(tmp_path: Path):
"""A,A,B,B,A with A/B far apart -> keep A0, B2, A4 (drops the repeats)."""
cands = _touch(tmp_path, 5)
survivors, dropped = frames._dedupe_by_deltas(
cands, [FLAT0, FLAT0, FLAT255, FLAT255, FLAT0], threshold=2.0
)
assert [s["index"] for s in survivors] == [0, 1, 2] # reindexed survivors
assert dropped == 2
def test_dedupe_threshold_is_inclusive(tmp_path: Path):
"""Delta exactly == threshold is treated as a duplicate (<=)."""
cands = _touch(tmp_path, 2)
a = bytes([0, 0, 0, 0])
b = bytes([8, 0, 0, 0]) # mean abs diff == 2.0
survivors, dropped = frames._dedupe_by_deltas(cands, [a, b], threshold=2.0)
assert dropped == 1
assert len(survivors) == 1
def test_dedupe_empty_and_single_are_noops(tmp_path: Path):
assert frames._dedupe_by_deltas([], [], threshold=2.0) == ([], 0)
one = _touch(tmp_path, 1)
survivors, dropped = frames._dedupe_by_deltas(one, [FLAT0], threshold=2.0)
assert dropped == 0
assert len(survivors) == 1
def test_dedupe_mismatched_thumb_count_is_noop(tmp_path: Path):
"""Fail open: if thumbs don't line up with candidates, change nothing."""
cands = _touch(tmp_path, 3)
survivors, dropped = frames._dedupe_by_deltas(cands, [FLAT0], threshold=2.0)
assert dropped == 0
assert len(survivors) == 3
# --- _thumb_frames + dedupe_perceptual: real ffmpeg over extracted JPEGs ------
def test_thumb_frames_match_candidate_count(cut_clip: Path, tmp_path: Path):
out = frames.extract_scene_candidates(str(cut_clip), tmp_path / "f", max_frames=None)
thumbs = frames._thumb_frames([Path(fr["path"]) for fr in out])
assert len(thumbs) == len(out)
assert all(len(t) == frames.DEDUP_THUMB * frames.DEDUP_THUMB for t in thumbs)
def test_dedupe_perceptual_collapses_static_clip(static_clip: Path, tmp_path: Path):
out = frames.extract(str(static_clip), tmp_path / "f", fps=4.0, max_frames=10)
n_before = len(out)
survivors, dropped = frames.dedupe_perceptual(out)
assert n_before > 1
assert len(survivors) == 1
assert dropped == n_before - 1
assert len(list((tmp_path / "f").glob("frame_*.jpg"))) == 1
def test_dedupe_perceptual_keeps_distinct_cuts(cut_clip: Path, tmp_path: Path):
"""Distinct color shots differ in luma, so frame-delta keeps them all."""
out = frames.extract_scene_candidates(str(cut_clip), tmp_path / "f", max_frames=None)
n_before = len(out)
survivors, dropped = frames.dedupe_perceptual(out)
assert dropped == 0
assert len(survivors) == n_before
# --- engine integration: dedup runs before the cap, reports deduped_count -----
def test_scene_engine_reports_zero_dedup_on_distinct(cut_clip: Path, tmp_path: Path):
out, meta = frames.extract_scene_or_uniform(
str(cut_clip), tmp_path / "f", fps=2.0, target_frames=50, max_frames=100,
)
assert meta["engine"] == "scene"
assert meta["deduped_count"] == 0
assert len(out) == len(list((tmp_path / "f").glob("frame_*.jpg")))
def test_uniform_fallback_dedupes_static(static_clip: Path, tmp_path: Path):
out, meta = frames.extract_scene_or_uniform(
str(static_clip), tmp_path / "f", fps=4.0, target_frames=12, max_frames=100,
)
assert meta["engine"] == "uniform"
assert meta["fallback"] is True
assert meta["deduped_count"] > 0
assert meta["selected_count"] == 1 # identical frames collapse to one
assert len(out) == 1
assert len(list((tmp_path / "f").glob("frame_*.jpg"))) == 1
def test_keyframe_uniform_fallback_dedupes_static(static_clip: Path, tmp_path: Path):
out, meta = frames.extract_keyframes(str(static_clip), tmp_path / "f", max_frames=50)
assert meta["engine"] == "uniform"
assert meta["deduped_count"] > 0
assert len(out) == 1
def test_dedup_false_disables_collapse(static_clip: Path, tmp_path: Path):
out, meta = frames.extract_scene_or_uniform(
str(static_clip), tmp_path / "f", fps=4.0, target_frames=12, max_frames=100,
dedup=False,
)
assert meta["deduped_count"] == 0
assert meta["selected_count"] > 1 # no collapse without dedup
assert len(out) > 1