Qwen Frame Selector
Let Qwen Tell You Which Frames Are Worth Keeping
- video
- images
- selected_frames
- scores_json
- summary
Stop sorting frames by hand
The annoying part of video work in ComfyUI isn't generating - it's deciding which frames deserve the effort. You generate a clip for img2img, or you want keyframes for i2v conditioning in Wan or LTX, and suddenly you're eyeballing a hundred near-identical frames hunting for the sharp ones. Qwen Frame Selector outsources that judgment call to a vision model. You feed it a video (or an existing image batch), it scores every frame on five quality dimensions, and hands you back just the ones that clear your bar - as a normal ComfyUI IMAGE batch you can preview, save, or pipe straight into img2img or a video sampler.
The fine print: this isn't a local model, it's a paid API call per frame. Know what you're signing up for.
How it actually works
Read the source and the README lines up well enough. In video mode it shells out to FFmpeg and extracts one frame every sample_rate frames (select=not(mod(n,N)), quality 2 JPEGs) into a temp folder. In image mode it skips extraction entirely, saves your IMAGE tensor to temp JPEGs, and analyzes everything you gave it - sample_rate is ignored there. Give it one or the other, never both, or it throws.
Every frame is base64-encoded and POSTed to OpenRouter's chat completions endpoint with qwen/qwen3-vl-235b-a22b-thinking - a real, current 235B-parameter vision model (22B active), served remotely. The prompt asks it to score sharpness, composition, aesthetic, technical, and content each 0–10, plus an overall score and a one-line reason, and return strict JSON. The node then drops anything below min_score, sorts by overall descending, keeps the top top_percent % (capped by max_frames), and re-sorts the survivors into chronological order before returning them.
One honest caveat the README dances around: content_diversity and keyframe_detection are just different prompt instructions. The selection code is always "top N by overall score" - there's no real deduplication or scene-change detection. Great for "best shot of this take," less useful than it sounds for "give me variety."
The inputs that matter
selection_criteria-balancedis the sane default;quality_focusedif you mostly care about sharpness/noise,aesthetic_focusedif you're curating for looks.sample_rate- extract one frame every N frames. This is your cost and time dial. 30 is fine for short clips; go 60–120 on anything long or you'll be waiting on a hundred sequential API calls.top_percent/min_score- the two filters. Start at 30% / 6.0 and tighten.videoORimages- exactly one.
Outputs: selected_frames (IMAGE, wire it to Preview Image / Save Image / anything), scores_json (the per-frame breakdown, handy to dump and inspect), and summary (a text report the node prints in its UI box).
Installing it
ComfyUI Manager → search "QwenFrameSelector" → install. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/eddyhhlure1Eddy/ComfyUI-QwenFrameSelector.git
cd ComfyUI-QwenFrameSelector
pip install -r requirements.txt
Restart, and the dependencies are mercifully light - just requests, pillow, numpy, torch, all of which your ComfyUI env almost certainly already has. No model downloads, because nothing runs locally. The one non-Python requirement is FFmpeg in your PATH (the install script checks for it), and an OpenRouter account with API credits. This node actually costs you money per run - budget a few cents for a short clip, more for a long one.
Where people get burned
The shipped API key. Open the node and the api_key field comes pre-filled with what looks like a real sk-or-v1-... key - it's hardcoded as the default in the source. Replace it with your own key, always. At best it's a stale placeholder the author forgot to remove; at worst it's someone's live credential that will bill their account every time you run a workflow. The custom node ecosystem has a real security trauma around trusting what you install (the LLMVISION malware incident), and a hardcoded API key is exactly the kind of hygiene slip to treat with suspicion. Paste in your own key, move on.
Silent API failures. If a call errors, the node quietly assigns the frame a default 5.0 score and keeps going. With min_score at 6.0 you can end up with a batch of junk that "passed" because everything fell back to defaults. If your picks suddenly look random, check your key, your credit balance, and your connection - the node won't tell you it failed.
Sequential slowness. Each frame is one HTTP round trip, ~2–4 seconds, and it doesn't batch. 500 frames is the practical ceiling the README quotes - at the top end that's 25+ minutes and hundreds of billed calls. Raise sample_rate, cap max_frames, and process long videos in chunks.
FFmpeg errors are self-explanatory: ffmpeg -version should print something. If it doesn't, install FFmpeg, add it to PATH, and restart ComfyUI.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| api_key | STRING | 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 | — |
| sample_rate | INT | 301–300 | Extract 1 frame every N frames |
| selection_criteria | COMBO | balanced | 5 options: quality_focused, aesthetic_focused, balanced, content_diversity, keyframe_detection |
| top_percent | FLOAT | 305–100 | Keep top X% of frames |
| min_score | FLOAT | 6.00–10 | Minimum quality score (0-10) |
| videoopt | VIDEO | Input video for frame extraction and analysis | |
| imagesopt | IMAGE | Input images for direct analysis (alternative to video) | |
| max_framesopt | INT | 00–1000 | Maximum frames to select (0 = unlimited) |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| selected_frames | IMAGE | — |
| scores_json | STRING | — |
| summary | STRING | — |