CV Stitch
Stitch a Batch of Photos Into One Panorama, Inside ComfyUI
- images
- masks
- panorama
- status
- success
Panorama stitching is one of those things that's trivial in every phone and annoying everywhere else. This node puts OpenCV's cv2.Stitcher in a ComfyUI graph: hand it overlapping frames, get one wide image back. That's the whole product, and it's genuinely useful - stitched panoramas make excellent wide reference plates, and you can pano-then-generate without leaving the graph.
The one design detail worth praising: stitching runs in a separate process, so it doesn't lock up the ComfyUI server while it works. Long stitches stay interruptible. That's more thought than most nodes in this ecosystem get.
How the input actually wants to be shaped
This is where people get stuck, and the tooltip is explicit about it.
images is a poly socket, so you can wire a ComfyUI IMAGE batch into it - but with an IMAGE (or LATENT) it uses frame 0 only, and a single frame produces no panorama at all (ERR_NEED_MORE_IMGS). To stitch several images you feed a batched NPARRAY: shape [B,H,W,C], all B frames stitched in order.
Convert with Image Batch → CV Batch (CV_ImageBatchToCV), and keep it uint8 so cv2 receives the dtype it expects. Two or more frames, in the order you want them stitched.
There's also an optional masks input: a coverage mask batch with the same batch dimension as the images, or a single mask shared by all frames. That's how you exclude a windshield edge or a lens hood from the seam search.
The knobs you'll actually touch
mode offers PANORAMA (the default) and SCANS, for flat scans, documents and other linear scenes. The difference is the camera model, and picking wrong is the usual cause of a spiral-shaped mess.
The three resolution inputs - registration_resol, seam_estimation_resol, compositing_resol - are in megapixels for each stage, and -1 means "let OpenCV pick automatically" (it scales its choice with the image count). Leaving them at -1 is the sane default. If a big stitch runs out of memory, this is the first lever: lower them, and only the compositing stage's value affects final sharpness.
pano_confidence_thresh (default 1) decides how confidently images must match before they're kept - raise it to get fewer, simpler seams.
wave_correction handles the horizontal drift you get from hand-held rotation; keep it on for panoramas.
interpolation_flags sets the compositing warp interpolator: INTER_LINEAR is fast and plenty, INTER_CUBIC or INTER_LANCZOS4 are sharper and slower.
Outputs
panorama (uint8 BGR NPARRAY), status (a string - OK, or one of cv2's ERR_* labels explaining what went wrong), and success (a boolean, true only when status is OK).
A failed stitch never halts the workflow: you get a copy of the first input frame plus success=false, so wire that into a branch with a control-flow node instead of letting a stitched-looking frame slip downstream. Preview the panorama with Preview CV Array, or convert with CV Array → Image - it defaults to BGR ordering, which is what the stitcher hands you.
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, recent V3-API ComfyUI. cv2.Stitcher itself is core OpenCV, but keep the contrib wheel anyway: installing a plain opencv-python over it shares site-packages/cv2 and strips the contrib submodules, breaking other nodes in this pack silently. tools/repair_opencv_contrib.py --check will tell you if that happened.
48_panorama_playground.json is the demo, and 01_install_example_inputs.json puts panorama_A.png and panorama_B.png in your input folder (run it, then reload the page so the dropdowns see them).
Where it bites
Stitching needs overlap. Two unrelated photos don't fail loudly - you get ERR_HOMOGRAPHY_EST_FAIL if you're lucky, or a confidently wrong panorama if you're not. Shoot with 30%+ overlap and avoid moving objects in the overlap region, since the seam finder will happily split a walking pedestrian in half.
And the pack's standing caveat, straight from its README: heavily LLM-assisted development, no support promised, workflows built to demonstrate rather than to ship. This node is a thin, well-behaved wrapper over standard OpenCV, so the caveat mostly costs you nothing here - just don't expect the rest of the pack to be equally tidy.
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| images | NPARRAY,IMAGE,MASK | Image batch to stitch. IMAGE/LATENT: frame 0 only. NPARRAY batch [B,H,W,C]: all B frames are stitched in order. Use 'Image Batch → CV Batch' to convert a ComfyUI IMAGE batch into a batched NPARRAY. 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. | |
| mode | COMBO | Stitcher_PANORAMA | Stitching mode: PANORAMA (default, general panoramas) or SCANS (scans/linear scenes). |
| registration_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for the registration stage (feature detection + matching). -1 = opencv default (auto-selects based on image count). |
| seam_estimation_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for seam estimation. -1 = opencv default (auto). Higher = more memory, lower speed. |
| compositing_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for compositing/warping phase. -1 = opencv default (auto). Higher = more memory, sharper result. |
| pano_confidence_thresh | FLOAT | 1.000–10 | Confidence threshold for seam estimation. Higher values produce fewer, longer seams (simpler blends). OpenCV default is 1.0. |
| wave_correction | COMBO | true | Enable wave correction (horizontal drift compensation). Recommended for panoramas with rotational motion. |
| interpolation_flags | COMBO | INTER_LINEAR | Interpolation method used for the compositing warp. INTER_LINEAR (default) is fast and adequate for most panoramas; INTER_CUBIC or INTER_LANCZOS4 are sharper but slower. |
| masksopt | NPARRAY,MASK | Optional coverage mask batch (same B dim as the image batch, or a single mask shared by all frames). 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. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| panorama | NPARRAY | Stitched panorama (uint8 BGR). On failure returns a copy of the first input frame. |
| status | STRING | 'OK' or an error label returned by cv2.Stitcher.stitch(). |
| success | BOOLEAN | True when stitching succeeded (status == OK). Use with IfElse to branch on success/failure. |