Video Stabilizer
The stabilizer that hands you a mask for the borders it creates
- frames
- stabilized_frames
- padding_mask
- info
Shaky footage is the quiet killer of video-to-video work. Feed a wobbling handheld clip into Wan, an AnimateDiff pass, or an inpaint step and the shake turns into motion blur, doubled edges, and seams that track nothing. So you reach for a stabilizer - and immediately hit the classic problem: stabilizing means re-framing, and re-framing crops into your shot. You lose FOV, and on a video you can't just "zoom out" to get it back.
The clever part of this node is that it solves that second problem for you. It wraps the MIT-licensed ComfyUI-Video-Stabilizer by nomadoor - the one from the well-received "stabilize without narrowing FOV" workflow that made the rounds on r/StableDiffusion - and adds an output the upstream had to be asked for: a padding mask that tells you exactly where the stabilizer introduced empty borders. Wire that mask into an inpaint pass and you can repaint the missing frame instead of losing it.
How it works
Three backends, one dropdown:
- classic - sparse GFTT feature tracking + Lucas-Kanade optical flow, runs on CPU, fast, needs well-textured footage.
- raft_flow - dense RAFT optical flow on GPU. Slower, but robust on texture-poor plates where feature tracking falls apart: snow, sky, water, out-of-focus shots.
- auto - picks
raft_flowwhen CUDA has ≥4 GB free and you feed it more than 24 frames, otherwiseclassic.
Whichever backend runs, the pipeline is the same shape: estimate the camera path, fit a transform to it, smooth that path over time, then reconstruct the frames - filling whatever the original didn't cover with your padding_color and reporting that coverage as a mask. The parameter names follow DaVinci Resolve conventions, so they read intuitively if you've touched a Resolve stabilizer.
The inputs that matter
You'll mostly live on preset and leave the rest alone - a preset overrides the manual sliders unless you set it to manual:
- preset -
handheld_light(default),handheld_heavy,vehicle,tripod_lock, ormanual. Good starting points for action-cam shake, drone footage, or a locked-off plate. - framing_mode -
cropzooms in to absorb shake,crop_and_padkeeps the zoom modest and fills the spill,expandpads without cropping.cropis where the FOV loss comes from. - transform_mode -
translation,similarity(default), orperspective. Step up toperspectivewhen the camera is actually rolling, not just bobbing. - frame_rate - set this near your source's real FPS. Smoothing is time-based, so a wrong FPS makes the wobble comeback; this bit the upstream author on 120 fps footage before the explicit FPS parameter existed.
- raft_iters and use_half - only matter for
raft_flow. More iterations = better flow, half precision = less VRAM.
Outputs and where they go
Three outputs: stabilized_frames (IMAGE), padding_mask (MASK), and an info string with the picked backend and timings. The padding_mask is the reason this node is worth reaching for over a plain stabilizer. It's 1 where the original frame didn't cover the canvas - i.e. the borders you need to fill. The pack's own pattern is:
stabilized_frames ──► InpaintCropProMEC (image)
padding_mask ──► InpaintCropProMEC (mask) → inpaint → stitch
Or feed the mask into a VACE outpainting pass on Wan, which is exactly the "keep your FOV while stabilizing" trick the upstream workflow thread demonstrated.
Install
It's part of the umbrella ComfyUI-CustomNodePacks pack, so install that and you get this node plus a few dozen masking/inpaint tools under the same roof:
cd ComfyUI/custom_nodes
git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
Or ComfyUI Manager → search "CustomNodePacks" → install, then restart. The pack's requirements.txt wants opencv-python and scipy - check what you already have before blindly pip install -r requirements.txt, because ComfyUI bundles its own torch/numpy and the README explicitly warns against clobbering them. Classic stabilization needs no model download.
Common issues
- "ComfyUI-Video-Stabilizer not vendored or import failed" - the node loads the upstream from
third_party/ComfyUI-Video-Stabilizer/, which the repo gitignores - if it didn't land with your clone, drop a checkout of the upstream repo in there. - Flow backend missing - if RAFT isn't available (
HAS_FLOW=false),autoandraft_flowsilently fall back to classic and log a warning. Classic still works; for flow, follow the upstream's weight instructions. - Edges flap or wobble - you've over-stabilized. Drop
strengthfrom 1.0 to 0.7–0.85. - Unwanted zoom-in - you're on
crop. Switch tocrop_and_padand inpaint thepadding_mask. - OOM on the flow backend - fewer frames, keep
use_halfon, or fall back to classic.
One honest caveat: this is a young node from a young pack - the repo moves fast and the README lags the code. The tooltips and the auto-generated node reference are more current than the marketing prose. But the core idea - stabilize, then repair the damage instead of cropping it away - is solid, and it's the rare stabilizer that treats the FOV problem as a first-class citizen.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| frames | IMAGE | — | |
| method | COMBO | auto | 3 options: auto, classic, raft_flow |
| preset | COMBO | handheld_light | Preset overrides the manual widgets below unless set to 'manual'. |
| frame_rate | FLOAT | 16.0 | — |
| padding_color | STRING | 127, 127, 127 | — |
| framing_mode | COMBO | crop_and_pad | 3 options: crop, crop_and_pad, expand |
| transform_mode | COMBO | similarity | 3 options: translation, similarity, perspective |
| camera_lock | BOOLEAN | false | — |
| strength | FLOAT | 0.700–1 | — |
| smooth | FLOAT | 0.500–1 | — |
| keep_fov | FLOAT | 0.600–1 | — |
| raft_iters | INT | 124–32 | RAFT iterations. Used only by method=raft_flow (and auto when it picks flow). |
| use_half | BOOLEAN | true | fp16 for RAFT. Used only by method=raft_flow. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| stabilized_frames | IMAGE | — |
| padding_mask | MASK | — |
| info | STRING | — |