FunPack StoryMem Keyframe Extractor
Distill a clip into the few frames that actually matter
- frames
- memory_frames
- keyframes
- keyframe_count
A 5-second clip is 100+ frames, and no "memory" system wants all of them. FunPack StoryMem Keyframe Extractor solves the right problem: it looks at a video batch and pulls out the few frames that represent the shot - the ones where the scene actually changes - using CLIP Vision similarity with an optional HPSv3 quality filter. If you're building a StoryMem loop, a recap, or a continuity bank, this is the node that decides what goes in the scrapbook.
How it works
Two filters stack up. First, CLIP Vision similarity: a frame becomes a keyframe only if it's different enough from the previous one. That's the similarity_threshold (default 0.9) - the tooltip puts it plainly: lower = more keyframes, higher = stricter, fewer. The comparison is what keeps a static shot from producing 50 "keyframes" of the same person standing still.
Second, optional quality: use_quality_filter (default off) gates frames through HPSv3, and quality_threshold (default 3) sets how strict the quality bar is. HPSv3 is a human-preference score for how aesthetically good a frame is, so this is how you keep a blurry or broken frame from poisoning your memory bank even when it's visually distinct.
Then max_keyframes (default 3) caps the haul, and memory_frames (optional IMAGE) lets you pass in previously extracted keyframes so the node can skip near-duplicates of frames you already have - the anti-repetition guard for multi-session continuity.
Outputs: keyframes (IMAGE batch) and keyframe_count (INT), so you can log or branch on how many you got.
The dependency gotcha (read this one)
The quality filter needs the optional hpsv3 package, which the README warns about in a big red box. hpsv3 pins transformers==4.45.2, and FunPack's LLM nodes (Prompt Enhancer, Story Writer, the advisor) need transformers >= 5.0. Install hpsv3 and you break those nodes. The pragmatic reading: if you use the LLM nodes, leave use_quality_filter off and skip hpsv3 entirely - the CLIP similarity pass is the more important filter anyway.
# only if you truly need HPSv3 and can live without the LLM nodes
pip install hpsv3 --no-build-isolation
Install
It ships in ComfyUI-FunPack:
cd ComfyUI/custom_nodes
git clone https://github.com/olivv-cs/ComfyUI-FunPack
pip install -r requirements.txt
or ComfyUI Manager → search "ComfyUI-FunPack". You'll also want a CLIP Vision model in ComfyUI's models/clip_vision folder, since the clip_vision input is an enum over your installed ones.
Where it fits
The output feeds the same places its sibling Last Frame Extractor feeds - continuity and memory - but smarter. Where the Last Frame Extractor hands you the literal end of a clip, this one hands you the representative frames, which is what a rolling memory bank wants so it doesn't store fifteen near-identical frames. In the StoryMem workflow it's the selector in front of the memory store: keep max_keyframes small, keep the threshold honest, and your long-video continuity gets a much cheaper, cleaner reference set.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | — | |
| clip_vision | COMBO | 0 options: | |
| max_keyframes | INT | 31–20 | Maximum number of keyframes to extract |
| similarity_threshold | FLOAT | 0.900–1 | CLIP similarity threshold (lower = more keyframes) |
| use_quality_filter | BOOLEAN | false | Use HPSv3 to filter low-quality frames (requires hpsv3 package) |
| quality_threshold | FLOAT | 3.00–10 | HPSv3 quality threshold (higher = stricter) |
| memory_framesopt | IMAGE | Previous keyframes to compare against (avoid duplicates) |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| keyframes | IMAGE | — |
| keyframe_count | INT | — |