Video Cut Group
Scene-cut detection that hands you the keyframes
- image
- image
- group_total
- start_index
- batch_count
If you've ever loaded a reference video for a vid2vid workflow and stared at a 400-frame batch, Video Cut Group is the answer you were looking for. It's a shot-boundary detector: feed it an IMAGE batch (any frames, usually from a video load), and it finds the scene cuts by comparing adjacent frames, then hands you back one representative frame per shot plus the metadata to slice the video however you want.
That's the workflow where this node earns its keep. Movement-transfer pipelines - WanAnimate, SCAIL, the various dance-replication setups - want a handful of keyframes from a source clip, not every frame dumped into a sampler. Instead of manually eyeballing frame indices, you run scene detection, get the first frame of each shot, and feed those into your reference encoder. Same logic applies to storyboard extraction.
One thing to know up front: the name is backwards in a useful way. It doesn't output the cut video segments - it outputs the keyframes (the first frame of each detected group). If you actually need the sub-clips, that's what the start_index and batch_count outputs are for: slice your original batch with those and you've got each shot as its own batch.
How it detects cuts
The core is SSIM - structural similarity between neighboring frames. When 1 - SSIM between frame N and N+1 exceeds the threshold, that's a hard cut and a new group starts at N+1. Bigger threshold = stricter = fewer cuts.
From there, two modes. Fast mode (fast = true) does a single, cheap SSIM pass with just threshold_base - great for a first look at a long video. The default precise mode is the interesting one: it Gaussian-blurs each frame at several kernel sizes (the kernel string, default 3, 7, 11, with sigma derived from each size), generates a spread of thresholds around threshold_base ± threshold_range across threshold_count values, runs every kernel×threshold combination, and then fuses all the detected cut points into one union list. It's voting by multiplication: a cut has to survive across multiple blur scales and thresholds to count. Flicker that trips one kernel won't trip all of them.
Two knobs then shape the result. min_frame_count (default 10) drops any cut that lands closer than that to the previous one - this is what stops a quick flash or blink from splitting a shot. max_frame_count (default 0, off) does the reverse, forcibly inserting a cut every N frames in any segment that runs longer than the cap.
The inputs and outputs that matter
- image - your frame batch (IMAGES). Load it with VideoHelperSuite's Load Video, or the pack's own Load Video / Load Video to Image.
- threshold_base (0.8) - the similarity bar. Raise it to only catch obvious hard cuts; lower it to catch more.
- min_frame_count (10) - merge cuts closer than this; also a floor on how small a group can be.
- fast (false) - precise multi-kernel detection vs. the quick single-pass version.
- add_frame / delete_frame - manual override: comma-separated frame indices to force in or out of the cut list (handles Chinese commas too, which is a nice touch).
Outputs: image (the first frame of each group - your keyframes), group_total (INT, how many shots), start_index (list) and batch_count (list) for slicing the original batch into the actual segments.
Installing it
Part of ComfyUI 1hewNodes - search "ComfyUI-1hewNodes" in ComfyUI Manager, or:
cd ComfyUI/custom_nodes
git clone https://github.com/1hew/ComfyUI-1hewNodes
Restart afterward. The relevant dependency is opencv-python (this node does its Gaussian blur and SSIM through cv2); it's in the pack's requirements.txt, so install that file if pip didn't pull it in. numpy comes with ComfyUI. The pack targets ComfyUI's newer comfy_api.latest node API, so a current ComfyUI build is expected.
Where people get burned
Precise mode is CPU-bound and it's not fast. The node moves the whole batch to CPU and runs B×kernels SSIM computations - a few hundred frames at multiple blur scales will sit there for a while. If you're iterating, run fast = true first to sanity-check the cut count, then switch to precise for the final pass. The node does run the work off the event loop (asyncio.to_thread), so your UI won't freeze, but the queue will.
Also remember the threshold is a similarity bar, not a sensitivity slider you should crank toward 0. Set threshold_base too low and every dissolve, camera whip, and lighting change becomes a "cut" - then min_frame_count is the only thing standing between you and a thousand groups. Tune it the other direction first: start strict, count the groups, and loosen only if real cuts are being missed.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| threshold_base | FLOAT | 0.800–1 | — |
| threshold_range | FLOAT | 0.050.01–0.2 | — |
| threshold_count | INT | 21–10 | — |
| kernel | STRING | 3, 7, 11 | — |
| min_frame_count | INT | 101–1000 | — |
| max_frame_count | INT | 00–10000 | — |
| fast | BOOLEAN | false | — |
| add_frameopt | STRING | — | |
| delete_frameopt | STRING | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| group_total | INT | — |
| start_index | COMFY_MULTITYPED_V3 | — |
| batch_count | COMFY_MULTITYPED_V3 | — |