Nodes/radiance/Stabilize
ComfyUI Node

Stabilize

Translation only, and it hands you the exact pixel offset

By FXTD-Studios·Created 8 months ago·Updated about 18 hours ago· 246
Stabilize
  • image
  • stabilized_sequence
  • displacements_xy
◄anchor_frame0►
◄max_shift64►

Shaky handheld plate, generated clip with a jittering camera, an upscale that wanders a pixel between frames - the fix is usually "stabilise it," and most people reach for a full tracking solution when all they actually have is translation. Stabilize does exactly one thing: it measures how far each frame slid, in sub-pixel units, and moves it back.

The mechanism

It's FFT phase correlation, which is the standard frequency-domain way to find the offset between two images: transform both, look at the phase difference, read the peak position. Because the peak is found on a sub-pixel interpolated surface, the correction isn't limited to whole pixels - that matters, because rounding a 0.4-pixel shift to 0 or 1 is exactly what makes a "stabilised" clip look like it's humming.

Three inputs. image is the sequence; shifts are measured on the channel average of each frame, so a strong colour cast won't skew the measurement the way a single-channel correlation might. anchor_frame (default 0) is the zero-based frame everything else aligns to, and it's forgiving - values past the end just use the last frame. max_shift (default 64, up to 512) is the largest correction applied per axis.

Read that last tooltip carefully, because it's a design decision and not a clamp for safety: "A larger measured shift is clamped to this rather than rejected." So a frame that moved 200 px while your max_shift is 64 gets 64 px of correction and stays visibly off. Nothing errors, nothing warns. If your stabilised sequence drifts at the start of a shot, raise max_shift before you go looking for a bug.

Outputs are stabilized_sequence and displacements_xy. That second one is the nice bit: it renders the per-frame shift as an image, red = x, green = y, in pixels. You can watch the camera move as a colour ramp, which is often faster than reading a report - and if you need the motion as data for something else, the same signal is right there.

The limit, stated plainly

Translation only. No rotation, no scale, no perspective. The node's own description says so, and it's the single thing to internalise: if your plate has a slow zoom or a roll, phase correlation will still find a translation, apply it, and leave you with the zoom or the roll intact - sometimes more visible than before, because now the easy motion is gone and the hard motion has the screen to itself.

For a locked-off shot with sensor shake, or a generated clip where each frame drifts a pixel or two, it's ideal and it's cheap. For a handheld walk-and-talk, you want a real tracker.

Also worth knowing: the pack's changelog records that the VFX nodes were rewritten to work a few frames at a time instead of holding the whole clip, and that Subpixel Stabilizer's output after the rewrite agreed with the old maths to within 7e-6. So this isn't a node that quietly changed your pixels when it got faster.

Install

Manager → search Radiance → Install → restart → refresh the browser. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/fxtd-studios/radiance.git
cd radiance
python -m pip install -r requirements.txt

Windows portable users: python_embeded\python.exe for the pip line. The pack's dependency list is long, but this particular node needs none of the model downloads - the heavy entries (OpenEXR, OpenColorIO, OpenImageIO) are for the HDR and colour side. If you're on Manager and the behaviour doesn't match the docs, run Update: the registry has been serving an older release than the README's 3.5.0.

Practical notes

Anchor on a frame you like - there's no "auto-pick the median frame" mode, and anchoring on a wildly unrepresentative frame (a whip pan, a flash) gives everything else a large correction to make. max_shift is your quality/cost dial in practice: high enough to catch the real motion, low enough that a single bad frame can't shove the whole sequence.

Finally, remember stabilisation is not smoothing. Each frame is aligned to the anchor independently, so the result can still breathe if the measurement is noisy. If the output jitters anyway, the input is probably low-contrast in the region you're measuring, and the answer is a better plate, not a bigger max_shift.

CategoryFXTD STUDIOS/Radiance/VFX

Inputs (3)

NameTypeDefaultDescription
imageIMAGEImage sequence to stabilise. Shifts are measured on the channel average of each frame.
anchor_frameINT00–1000Zero-based index of the frame every other frame is aligned to. Values past the end use the last frame.
max_shiftINT644–512Largest correction applied, in pixels per axis. A larger measured shift is clamped to this rather than rejected.

Outputs (2)

NameTypeDescription
stabilized_sequenceIMAGE—
displacements_xyIMAGE—