CS Spatial Stabilize
CS Spatial Stabilize gives you one steady crop for the whole clip
- image
- mask
- IMAGE
- STABLE_DATA
- MASK
If you've ever tried to fix something on a face in a video - an eye that melted, a watermark riding on someone's shirt, a flickering artifact - you know the core problem: the thing you want to fix is moving. Feed raw, wandering crops into a per-frame inpaint or restore model and the regenerated content jitters with the bounding box, swimming worse than the original defect.
CS Spatial Stabilize is the front half of an answer. It's one node in chflame163's ComfyUI_CineStyle suite (the "😺dzNodes/CineStyle" menu), and it does one job cleanly: hand it a video as an IMAGE batch plus a per-frame MASK of your subject, and it warps every frame so the subject sits centered and equal-area scaled inside one fixed-size crop for the whole clip. Its sibling, CS Spatial Restore, pastes your edited crop back into the original frame positions. Together they're the video version of the crop-and-stitch loop still-image folks have run for years - crop tight, re-render at full resolution, composite back - with the crop held still so temporal edits don't shimmer.
How it works
Under the hood it's a per-frame analysis pass followed by a fixed envelope - smarter than a naive "track the bbox":
- Each mask frame gets a Gaussian pre-blur (
mask_blur_sigma) and is thresholded, so a few stray mask flecks don't decide your crop. - The node picks the anchor frame: the fully-visible frame whose mask has the largest area.
- For every frame it computes the mask's center and a scale of
sqrt(anchor_area / frame_area)- the equal-area trick. A person walking toward the camera stays constant in the crop instead of ballooning. - Frames where the mask is missing or touches an image edge get their center and scale linearly interpolated from neighbors instead of being dropped.
- A centered moving average (
average_frameswide) smooths X, Y, and scale so the crop glides rather than snaps; first and last frames stay locked so the transform stays reversible. - The output crop is sized from the largest stable bounding box, padded by one
multipleper side plus your margin, and rounded up to themultiple.
The inputs that matter
You'll actually touch a handful of these:
- image and mask - the video frame batch and its matching mask batch (same spatial size). CineStyle's own example workflow pulls masks from its CS Video Segment nodes, but any mask source works. Extra mask frames past the image batch are ignored; missing ones are treated as empty.
- multiple (default 32) - crop width/height rounded up to a multiple of this, keeping downstream latent ops, tiles, and codecs on an even grid. Leave it alone unless you know why you're changing it.
- Crop Margin Per Side (%) (default 30) - breathing room on each side, as a percentage of the biggest mask width/height. If your local edit bleeds past the mask, more margin hides it; too little and the restore seam lands on the subject.
- Average Frames (default 8) - the de-jitter window. 1 disables smoothing. Crank it for shaky handheld footage; keep it low when the subject genuinely changes speed.
- Mask Blur Sigma (default 6) - only used to find the box, not on the output mask. If your masks are already clean, a smaller value keeps the crop honest.
Outputs are IMAGE (the stabilized crop batch), MASK (the same crop, mask aligned to it), and STABLE_DATA - the custom type carrying the transform matrices, wired straight into CS Spatial Restore. No model file needed; this is pure tensor math.
Installing and the honest gotchas
Install is the usual dance: ComfyUI Manager → search "ComfyUI_CineStyle", or git clone https://github.com/chflame163/ComfyUI_CineStyle.git into ComfyUI/custom_nodes/, then restart. Caveat: the pack's requirements.txt drags in heavy stuff (transformers, hydra, iopath, av…), but that's for its segmentation and audio nodes - the spatial pair pulls only what ComfyUI already ships. Expect the whole list to install regardless.
Where people get burned:
- The node needs at least one non-empty mask frame and one fully-visible one - a mask touching any image edge is treated as out-of-frame and can't anchor the scale. If every frame clips the subject at the border, it errors.
- Mask and image dimensions must match, and the mask is interpreted as
[frames, H, W]. Get the batch dims wrong and you'll chase weird crops. stable_dataand the crop envelope depend onmultipleand the margin - change those mid-clip and everything downstream referencing the old STABLE_DATA goes stale.
It's young and niche - effectively no community write-ups yet - so the README and example workflow are your best map. If your subject disappears behind something, those frames get interpolated rather than truly tracked - plan for that in the mask.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | Source video frames as a standard ComfyUI IMAGE batch. | |
| mask | MASK | Corresponding MASK batch. Frames beyond the IMAGE batch are discarded; missing frames are treated as empty masks. | |
| multiple | INT | 321–1024 | Crop width and height are rounded upward to this multiple. |
| crop_margin_percent | FLOAT | 30.00–500 | Extra margin on each side as a percentage of the maximum stabilized BBox; one multiple per side is always added first. |
| average_frames | INT | 81–999 | Centred moving-average window applied to the interpolated X, Y, and Scale values while the first and last frames remain locked; 1 disables smoothing. |
| mask_blur_sigma | FLOAT | 6.00–256 | Gaussian sigma applied only before the > 0.5 BBox threshold. This suppresses isolated mask pixels; 0 disables pre-blur. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| STABLE_DATA | CS_SPATIAL_STABLE_DATA | — |
| MASK | MASK | — |