Cross-BiMagnitude Stitch
Two images that don't line up? This node finds the offset for you
- reference_image
- target_image
- stitched_image
- offset_dy
- offset_dx
You've got two images that are supposed to overlap - two halves of a scan, two frames of a camera sweep, two tiles of a panorama - and they're off by a few dozen pixels. You could drop them into an editor and nudge by hand, or you can drop this node into the graph and let it figure out the shift, then blend the overlap so there's no visible seam. That's the whole job of Cross-BiMagnitude Stitch, the main node in the bemoregt/ComfyUI_Cross-Bi-Magnitude pack. Feed it a reference and a target, get back a single stitched image on an auto-sized canvas.
How it actually works
The trick is phase correlation, a classic FFT method for finding the translation between two images. FFT both images, multiply one spectrum by the conjugate of the other, keep only the phase, inverse-FFT - you get a correlation map whose peak marks the shift. It's fast and it shrugs off brightness differences, but it's brittle when there's noise.
This pack adds a twist. Instead of pure phase, it weights the correlation by a term it calls the cross-bimagnitude, derived from the cross-bispectrum - a third-order statistic (higher-order statistics is the "HOS" in the README, if you ever wondered). Concretely, the weight is |X|^(2/3) · |Y|^(1/3) where X and Y are the two FFTs; the 1/3 exponent reflects that a bispectrum is a product of three spectra. The pitch is that third-order structure is more robust to Gaussian noise than plain second-order phase correlation. In practice, you mostly don't care about the math - you care that it finds (dy, dx) and stitches. The full 4-D bispectrum would be prohibitively expensive, so this uses a diagonal-slice approximation that keeps the weighting without the cost. It's a real, published line of research, implemented in a single cross_bimagnitude_node.py with numpy and scipy.
The inputs that matter
The two image sockets are the only wiring you strictly need:
reference_image- defines the output coordinate frame. It stays put.target_image- gets shifted onto the reference.
After that, the only settings you'll touch are:
use_auto_align(default on) - run the FFT alignment, or turn it off and drivemanual_dy/manual_dxyourself. That's your escape hatch when auto picks a wrong offset.blend_mode-feather(default, smooth falloff, recommended for most cases),linear(50/50 wherever the images overlap), oroverlay(target slapped on top, no blending).feather_px(default 40) - how wide the feather transition is in pixels.
The outputs are stitched_image, plus offset_dy and offset_dx - the detected shift as plain integers, handy if you want to log or reuse the offset elsewhere. Both images come out as single-frame batches; only the first frame of each input is processed.
Installing it
It's a tiny pack - one commit, no requirements.txt, nothing to pip install. numpy, scipy, and torch all ship with a standard ComfyUI. Grab it through ComfyUI Manager (search "Cross-BiMagnitude") or:
cd ComfyUI/custom_nodes
git clone https://github.com/bemoregt/ComfyUI_Cross-Bi-Magnitude
Then restart ComfyUI. Both nodes appear under image/Cross-BiMagnitude.
Where people get burned
- Translation only. Rotation, scale, and perspective are not compensated. If your two images are rotated relative to each other, auto-align will find a best-effort shift and the seam will be visibly wrong. Pre-warp them first, or fall back to
manual_dy/manual_dx. - Overlap assumption. The algorithm wants a meaningful shared region. With zero overlap it still returns a "best" translation, and you'll just get the images placed side by side.
- Big size differences degrade accuracy. FFT zero-padding handles different sizes, but keep them roughly comparable.
- The README's own install command is a trap. It tells you to
cp -r cross_bimagnitude_stitch ..., but no folder by that name exists in the repo - the repo root is the package. If you copy it manually, keep the folder namedComfyUI_Cross-Bi-Magnitude, which is what the node module expects.
Honestly, if your frames already line up, you don't need this - a plain composite works. It earns its keep exactly when the offset is unknown, too big to guess, or slightly different every run.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| reference_image | IMAGE | — | |
| target_image | IMAGE | — | |
| blend_mode | COMBO | 3 options: feather, linear, overlay | |
| feather_px | INT | 401–256 | — |
| manual_dy | INT | 0-4096–4096 | — |
| manual_dx | INT | 0-4096–4096 | — |
| use_auto_align | BOOLEAN | true | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| stitched_image | IMAGE | — |
| offset_dy | INT | — |
| offset_dx | INT | — |