Nodes/ComfyUI CV/CV Align MTB To Reference
ComfyUI Node

CV Align MTB To Reference

When you don't want the frames negotiating with each other

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
CV Align MTB To Reference
  • image
  • image
◄reference_index0►
◄max_bits4►
◄exclude_range4►
◄cuttrue►

Its sibling, CV Align MTB, runs OpenCV's joint process() - the frames optimise a common alignment together. This node takes the other approach: you name one frame as the reference, and every other frame is shifted independently until it best matches that. Same median-threshold-bitmap machinery, different contract. The reference frame passes through untouched, which means the output has a fixed point you can point at.

Why you'd reach for it

  • Video stabilisation against frame 0. Independent per-frame shifts don't drift or average each other's errors, so the result is reproducible: same batch, same reference, same output, every time.
  • Brackets where one exposure is the keeper. In a hand-held bracket of sky and shadow, the mid-exposure is usually the sharpest and least noisy. Make it reference_index and everything else registers to it, rather than to a consensus the dark frame gets a vote in.
  • Any time you need a deterministic answer. Two-frame joints are sensitive to which pair you compare; a fixed reference removes the ambiguity.

How it works

Each frame is reduced to grayscale uint8 and to a median threshold bitmap - "brighter or darker than this frame's grayscale median", which is exposure-invariant - and calculateShift finds the best integer translation against the reference's bitmap. If that shift is (0, 0) the frame is passed through unmodified; otherwise shiftMat applies it. Then the batch is reassembled in the input's own format.

That last part matters: the node accepts IMAGE, MASK, NPARRAY (list or 4-D) or LATENT and gives you back whatever you put in. Nothing is normalised to uint8 BGR permanently - a mask batch comes back as a mask batch, a latent batch as a latent batch.

The inputs that matter

  • image - the batch of 2+ frames to align.
  • reference_index - 0-based index of the frame everything else is matched against, default 0. It is not shifted. An out-of-range index raises rather than quietly falling back to frame 0, which is the behaviour you want.
  • max_bits - bit depth of the median bitmaps, default 4, range 1–32. Values 2–4 give reliable results in this OpenCV build; higher may return incorrect shifts on some inputs - the pack's own tooltip says that, and the code enforces it by clamping to 4 regardless. Don't crank this expecting precision.
  • exclude_range - half-width of the pixel band around the median excluded from the bitmap, default 4. Raising it throws away more noise-sensitive pixels and makes the match more robust, up to the point where there's nothing left to match.
  • cut - exclude the darkest and brightest pixels, on by default.

Output: image, the aligned batch, same format and size, same frame count and order.

Progress and previews

Because alignment is per-frame rather than one bulk call, the node drives a real ComfyUI progress bar and pushes a preview roughly twenty times across the run - so you can watch it work on a long batch instead of staring at a blank node. Small thing; it's the difference between "is this hung?" and knowing.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

Restart, or install via ComfyUI Manager by searching ComfyUI CV. The pack's single hard dependency is opencv-contrib-python-headless~=5.0.0.93, and it needs Python 3.12+ plus a ComfyUI built on the V3 node API. Contrib is not optional for the pack as a whole (the xphoto/ximgproc nodes vanish without it) even though this node only needs core cv2.

Traps

  • max_bits above 4 is wasted. The node clamps it and the tooltip warns you; a high value on an input the build can't handle gives you a wrong shift rather than an error, so just leave it at 4.
  • A bad reference poisons the whole batch. If frame 0 is the blurriest one in the set, every other frame will dutifully blur-align itself to it. Pick the sharpest middle exposure.
  • Translation only. Wind, water, a hand-held tilt, a subject that moved between shots - none of that is corrected, and no amount of tuning will change it. Align your bracket, then fuse.
  • Order is preserved. The reference stays at its own index rather than being pulled to the front, which matters if a downstream node expects exposure_times in the same order as the frames you stacked (the pack's HDR merge nodes do).
Categoryimage/CV/hdr

Inputs (5)

NameTypeDefaultDescription
imageCOMFY_MATCHTYPE_V3Batch of 2+ images to align. Accepts IMAGE, MASK, NPARRAY (list or 4-D array), or LATENT. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
reference_indexINT0Index of the frame to use as the alignment reference (0-based). This frame is not shifted; all others are shifted to match it.
max_bitsINT41–32Bit depth for the median bitmaps used by calculateShift. Values 2-4 give reliable results in this OpenCV build; higher values may return incorrect shifts on some inputs.
exclude_rangeINT40–128Half-width of the pixel-value range excluded from the median threshold bitmap.
cutBOOLEANtrueExclude the darkest and brightest pixels from the median threshold bitmap.

Outputs (1)

NameTypeDescription
imageCOMFY_MATCHTYPE_V3Aligned images in the same format as the input.