Nodes/ComfyUI CV/CV Stitch Canvas
ComfyUI Node

CV Stitch Canvas

The Canvas Math Behind a Two-Image Stitch (and Why You Need It)

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Stitch Canvas
  • image_warp
  • image_ref
  • homography
  • warp_matrix
  • ref_matrix
  • dsize
  • width
  • height
  • size
◄max_size8192►

Stitching two images by hand is where most people first hit the boring geometry problem: once you know the homography that maps image B into image A's frame, where do you put the result? Warp B without thinking and half of it lands at negative coordinates and gets clipped away.

This node answers that. Given the two images and the homography between them, it projects the warp image's corners, unions that rectangle with the reference image's rectangle, and hands you the output canvas plus the two matrices that place each image on it.

Use it for any two-image composite where you already have the alignment - matched features plus CV Find Homography, a manual offset, an estimated planar transform - and you want a proper canvas instead of two clipped halves.

What it gives you

warp_matrix is the homography pre-multiplied by the canvas translation, i.e. the matrix that maps the warp image into canvas coordinates. ref_matrix is a pure translation that places the reference image on the same canvas. size is the canvas (width, height) as one composite value you can wire straight into a dsize input, with width, height and a text dsize string alongside for the free-text parameters that haven't been converted to sockets.

image_warp and image_ref feed in only for their sizes - the node reads dimensions, not pixels - so you can hand it a downscaled stand-in if that's convenient. homography is the 3x3 mapping warp-image coordinates into reference-image coordinates, exactly what CV Find Homography produces.

max_size (default 8192) caps the canvas in each axis. That is not paranoia: a bad homography projects corners kilometres away, and without a cap you would be asking OpenCV to allocate a gigapixel image. With the cap, the reference frame is still guaranteed to stay fully contained, so a failure mode here is a truncated canvas, never an out-of-memory crash.

The part everyone gets wrong

When you actually apply these matrices with cv2_warpPerspective, set borderMode=BORDER_CONSTANT. The default BORDER_DEFAULT reflects the image into the empty canvas area - mirror ghosts in your output, and worse, a coverage mask that no longer means anything, which wrecks the feather blend downstream. The docs on this node call that out specifically, and it's the kind of thing you'd otherwise spend an hour blaming on the homography.

The intended finish is OpenCV Feather Blend: warp both images onto the same canvas with their own matrices, blend with coverage masks, done. Both warp_matrix and ref_matrix are 3x3, so they slot into the same warp node with different dsize handling.

Install

ComfyUI Manager → search ComfyUI CV (publisher bmad4ever), or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Restart after. Python ≥ 3.12 and a recent ComfyUI built on the V3 node API. The contrib wheel matters here only because of the rest of the pack - a plain opencv-python install shares site-packages/cv2 and silently empties the contrib submodules, breaking sibling nodes. tools/repair_opencv_contrib.py --check / --apply.

Related examples: 48_panorama_playground.json for the stitcher route, and the quad-warp / paste-into-scene workflows that use the same canvas idea. 01_install_example_inputs.json copies the sample images in - run it, then reload the page.

Where it bites

Not the node - the input. If CV Find Homography came back with found=false and an identity matrix, you'll get a canvas that's just the union of two overlapping identical frames and an output that looks like a double exposure. Check found, and filter to RANSAC inliers before solving.

Also don't reach for this when a plain cv2_warpPerspective with the reference size will do. If the second image is fully inside the first one's bounds after warping, the canvas is the reference frame and this node is extra machinery.

And the pack's standing disclaimer, since it applies to everything here: bmad4ever's README says this is a personal, heavily LLM-assisted project, not production-grade, no support planned. This particular node is small, well-documented arithmetic with a guard rail, so it's one of the safer bets in the pack.

Categoryimage/CV/features

Inputs (4)

NameTypeDefaultDescription
image_warpNPARRAY,IMAGE,MASKImage that the homography maps into the reference frame (only its size is used here). 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.
image_refNPARRAY,IMAGE,MASKReference image; stays axis-aligned on the canvas (only its size is used here). 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.
homographyNPARRAY3x3 matrix mapping image_warp coordinates to image_ref coordinates.
max_sizeINT8192256–32768Canvas width/height cap in pixels - protects against degenerate homographies that would project corners kilometers away.

Outputs (6)

NameTypeDescription
warp_matrixNPARRAY3x3 matrix that places image_warp on the canvas (translation x homography).
ref_matrixNPARRAY3x3 translation that places image_ref on the canvas.
dsizeSTRING'(width, height)' canvas size literal, for the composite params that are still free-text.
widthINT—
heightINT—
sizeCV_TUPLEThe canvas (width, height) as ONE composite value - wire it into cv2.warpPerspective's dsize.