KEEP Image Sequence
Why your video faces flicker, and the checkbox that silently does nothing
- images
- keep_model
- processed_images
Run a per-frame face restorer over a clip and you get the horror show: the face is fixed and it boils. Every frame gets its own independent guess at that person, so the eyes shift, the nose changes width, and the whole face shimmers against a perfectly stable background. Detectors don't help - frame in, crop out, no memory.
KEEP exists because of that. The paper is Kalman-Inspired Feature Propagation for Video Face Super-Resolution, and propagation is the point: the network sees a run of frames together and carries information forward, so frame 200 benefits from frame 199. This node is where that machinery gets used as designed.
How it works, in order
Four stages, each visible in your results.
Detect across the whole clip first. Every frame is walked for five landmarks per face, nothing restored yet - it's building a map of where each face is over time.
Smooth that map. Detection gaps get filled by linear interpolation, then the landmark coordinates get a Gaussian filter along the time axis. That's why faces in a KEEP clip sit still: the crop window itself is de-jittered, not just the pixels inside it.
Track separate people. With only_center_face on this is trivial. With it off, faces are matched frame to frame by landmark centroid distance (Hungarian assignment, 75px threshold), so two people moving apart stay two tracks rather than swapping identities halfway through.
Restore as one batch. Every cropped face from every frame is stacked into one tensor and pushed through the KEEP network together, chunked so VRAM survives, then pasted back through the stored affine transforms over an upscaled background.
That batch step is the whole point: twenty frames of one face are a temporal sequence, not twenty unrelated images.
The inputs
images is an IMAGE batch - frames from the core Load Video node or VideoHelperSuite's loader. keep_model carries your detector plus both upscalers.
max_clip_length (1–100, default 20) is the one to understand. The tooltip says frames per batch, but the code chunks the flattened list of faces: one face per frame and that's effectively frames-per-chunk, three faces per frame and you get through about seven frames per chunk. Drop to 8–12 near your VRAM ceiling, raise toward 40+ on a big card. This is the crash-or-succeed knob.
final_upscale_factor (0.5–4.0, default 1.0) behaves as on the single-image node. Keep it at 1.0 if you only want the face repairs.
only_center_face defaults true and is right for talking heads. Turn it off for two-handers, but know it's centroid matching, not recognition - two people crossing close together is exactly where it can still swap.
draw_bounding_box is a debug aid with no cinematic use. Output is processed_images, an IMAGE batch the same length as the input; wire it to Video Combine or Save Image.
Don't touch has_aligned_frames
Leave it off. Not "usually leave it off" - off.
Frames flagged pre-aligned get resized to 512, restored - and then thrown away in the paste loop, which short-circuits on if num_faces_this_frame == 0 or has_aligned_frames: output_frames_cv2.append(bg_img_final). You get the upscaled background and none of the restoration. The author's own comment is "a little simplified, aligned case could be handled better". Already-512 frames belong in KEEP Single Image, one at a time.
Install
Manager → ComfyUI-KEEP, or:
cd ComfyUI/custom_nodes
git clone https://github.com/wildminder/ComfyUI-KEEP
cd ComfyUI-KEEP
grep -v -E '^(torch|torchvision)$' requirements.txt > /tmp/keep-req.txt
pip install -r /tmp/keep-req.txt
The grep isn't me being precious: requirements.txt lists bare torch and torchvision, and installing those into an existing ComfyUI environment can quietly swap your CUDA build. Weights download into models/keep_models/KEEP/ and models/facedetection/; fallback is the upstream v1.0.0 release.
What goes wrong
RAM before you even start. Every frame is converted to a numpy array up front and kept until the paste stage - a 300-frame 1080p clip is a couple of gigabytes of host RAM on top of the tensors ComfyUI already holds. Cut long clips into 100–200 frame chunks.
Flicker that smoothing can't fix. KEEP debuted on r/StableDiffusion in August 2024 to real interest and a sharp caveat in the same thread: eyes moved around and fine detail came out softened against ground truth. Landmark smoothing fixes crop jitter, not a network unsure about pupils. Expect to fix eyes on a close-up.
The trade against the alternative. Per-frame detailers - FaceDetailer, ADetailer, the Wan one-step video detailers - give crisper skin and lashes, and they flicker, because per-frame is all they are. KEEP gives stability and identity instead. Check a three-second cut before committing an hour of render.
Detector misses become visible holes. Lose the face for twenty consecutive frames and the interpolation fills the gap with a straight line between two distant points - a smear of restored face sliding over a shoulder. Not a bug in KEEP, a detection failure amplified. Try a heavier detector in the loader or trim the clip.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| keep_model | KEEP_MODEL_PACK | — | |
| final_upscale_factor | FLOAT | 1.00.5–4 | The final upscaling factor for the output frames. They will be resized to this scale after processing. |
| has_aligned_frames | BOOLEAN | false | Check if the input frames are already aligned 512x512 faces. |
| only_center_face | BOOLEAN | true | If frames have multiple faces, only process the one closest to the center. |
| draw_bounding_box | BOOLEAN | false | Draw a bounding box around the detected face on the output frames. |
| max_clip_length | INT | 201–100 | Maximum number of frames to process in a single batch to manage VRAM. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| processed_images | IMAGE | — |