CV Stitch (Advanced)
Every Panorama Knob OpenCV Hides Behind `cv2.Stitcher`
- images
- masks
- panorama
- status
- success
cv2.Stitcher is a friendly front door: one call, sane defaults, no opinions required. It's also a black box, and when the black box produces a warped spiral or a seam straight through someone's face, the front door gives you nothing to turn.
This node is the same pipeline with the walls off. It drives cv2.detail directly - feature detection, matching, camera estimation, bundle adjustment, warp type, exposure compensation, seam finding, blending - so every stage that was implicit becomes a widget. The stage order and parameter set deliberately follow OpenCV's own samples/python/stitching_detailed.py, which is the reference implementation most people never find.
You won't touch most of these. You will touch about four.
The four that fix things
warp_type decides the projection. Spherical is the default and right for a wide sweep; plane is the one to reach for when your images are flat and coplanar (document, wall, poster) - spherical on a flat subject is what produces the infamous bow-tie. Cylindrical keeps vertical lines vertical, which matters for architecture. Fisheye/stereographic/mercator exist too.
seam_finder picks how the cut line is chosen. gc_colorgrad (GraphCut on colour + gradient) is the default and generally the best; dp_* (dynamic programming) is faster and less clever; voronoi is crude but very fast; no skips it.
blend_type and blend_strength do the final merge. multiband (Laplacian pyramid) is the quality answer and the default; feather is linear; no is a hard cut. blend_strength (default 5) controls how soft the transition is.
exposure_compensation fixes the brightness mismatch your camera's auto-exposure created between frames - gain_blocks (local block gain) default, gain for a single global gain, channel* variants for colour casts, no to skip.
The rest, briefly
features (ORB fast, SIFT better) and matcher (best_of_2_nearest, the range-limited variant, or the affine one for SCANS-style planar captures) drive matching; match_conf at -1 auto-picks 0.3 for ORB and 0.65 for SIFT. estimator is homography for perspective, affine for flat scenes. bundle_adjuster refines camera parameters - ray default, reproj alternative, no to skip. pano_confidence_thresh excludes images that don't match confidently enough.
wave_correction (auto/horiz/vert/no) kills the drift from hand-held rotation - leave it on auto for panoramas. registration_resol, seam_estimation_resol and compositing_resol are megapixels per stage at -1 = full resolution; lowering them is your memory lever, and only the compositing value affects final sharpness. interpolation_flags picks the warp interpolator. The three expos_comp_* inputs matter only for the channel-based compensators.
And images is the same poly socket as CV Stitch: an IMAGE or LATENT contributes frame 0 only, so for a multi-image stitch use a batched NPARRAY from Image Batch → CV Batch (uint8, in stitch order), with an optional masks batch alongside it.
Outputs are panorama (uint8 BGR), status (an OK or ERR_* label from the pipeline) and success. A failure returns a copy of the first input frame - branch on success.
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. The detailed stitching pipeline is core OpenCV, but keep the contrib wheel: a plain opencv-python overwrites the shared site-packages/cv2 and quietly empties the contrib submodules, taking other nodes in the pack with it. tools/repair_opencv_contrib.py --check / --apply.
48_panorama_playground.json wires this and the list variant; 01_install_example_inputs.json copies panorama_A.png / panorama_B.png into your input folder (run it, then reload the page). The stitching runs in a separate process, so it stays interruptible.
Where it bites
Start from the defaults and change one thing at a time. Twenty knobs interacting is a debugging nightmare, and the failure modes are visual: planes fold, seams crawl, exposure bands. If a stitch that worked with cv2.Stitcher goes bad here, the difference is almost always warp_type or the resolutions not being at -1.
Honest framing, from the pack's own README: personal project, heavy LLM assistance, no support promised, workflows built for demonstration rather than production. This node's parameter set is copied from OpenCV's official sample, which makes it the most defensible thing in this corner of the pack - the risk is in the preset values, not the mechanism.
Inputs (22)
| 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. | |
| features | COMBO | orb | Feature detector type. ORB is fast and universally available. SIFT is more distinctive but slower. |
| matcher | COMBO | best_of_2_nearest | Feature matcher. best_of_2_nearest (default): standard pairwise matching. best_of_2_nearest_range: limits matching to neighbors within range_width. affine_best_of_2_nearest: for affine transformations (SCANS mode). |
| match_conf | FLOAT | -1.00-1–1 | Feature match confidence threshold. -1 = auto (0.3 for ORB, 0.65 for SIFT). Higher = fewer, more reliable matches. |
| range_width | INT | -1-1–100 | Range width for best_of_2_nearest_range matcher. -1 = match against all images. Only used when matcher is best_of_2_nearest_range. |
| estimator | COMBO | homography | Camera estimator. homography (default): perspective transforms between images (PANORAMA mode). affine: affine transforms (SCANS mode, planar scenes). |
| bundle_adjuster | COMBO | ray | Bundle adjustment cost function. ray (default): ray-based error. reproj: reprojection error. affine_partial: partial affine refinement. no: skip bundle adjustment. |
| pano_confidence_thresh | FLOAT | 1.000–10 | Confidence threshold for feature matching. Images with confidence below this are excluded. Also passed to the bundle adjuster. |
| warp_type | COMBO | spherical | Projection warp type. spherical (default): good for wide panoramas. plane: no warping (flat scenes). cylindrical: vertical lines stay straight. fisheye/stereographic/mercator: specialized projections. |
| wave_correction | COMBO | auto | Wave correction for horizontal/vertical drift. auto (default): auto-detect direction. horiz/vert: force direction. no: disable. |
| registration_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for the registration stage (feature detection + matching). -1 = full resolution. |
| seam_estimation_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for seam estimation and exposure compensation. -1 = full resolution. |
| compositing_resol | FLOAT | -1.00-1–100 | Resolution in megapixels for compositing/warping phase. -1 = full resolution. |
| interpolation_flags | COMBO | INTER_LINEAR | Interpolation method for warping. INTER_LINEAR (default) is fast; INTER_CUBIC or INTER_LANCZOS4 are sharper. |
| exposure_compensation | COMBO | gain_blocks | Exposure compensation method. gain_blocks (default): local block gain. gain: global per-image gain. channel: per-channel gain. channel_blocks: local per-channel gain. no: disable. |
| expos_comp_nr_feeds | INT | 11–10 | Number of exposure compensation feeds. Only used for channel / channel_blocks compensators. |
| expos_comp_nr_filtering | INT | 21–10 | Number of filtering iterations for exposure compensation gains. Higher = smoother gain maps. Default 2. Only used for channel / channel_blocks compensators. |
| expos_comp_block_size | INT | 321–256 | Block size (pixels) for block-based exposure compensators (gain_blocks, channel_blocks). |
| seam_finder | COMBO | gc_colorgrad | Seam finding method. gc_colorgrad (default): GraphCut with color + gradient cost. gc_color: GraphCut, color only. dp_color / dp_colorgrad: dynamic programming. voronoi: Voronoi diagram. no: skip. |
| blend_type | COMBO | multiband | Blending method. multiband (default): Laplacian pyramid blend (best quality). feather: linear distance blend. no: direct cut. |
| blend_strength | FLOAT | 5.00–100 | Blending strength [0-100]. Higher = softer transition at seams. Default 5.0. |
| 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 the detail pipeline. |
| success | BOOLEAN | True when stitching succeeded (status == OK). Use with IfElse to branch on success/failure. |