CV Find Homography (RANSAC)
Teach two photos that they're the same wall
- points_a
- points_b
- homography
- inlier_mask
- inlier_count
- found
A homography is the maths behind "that poster is still the same poster, just photographed from a different spot." You give this node two sets of matched points - the same features found in image A and image B - and it hands back the 3×3 matrix that maps one onto the other. With that matrix you can warp A into B's frame, drop a virtual cube on the poster, or stitch a panorama. It's cv2.findHomography with the plumbing and the failure handling done for you.
This is classic computer vision, not a diffusion pass. ComfyUI CV is bmad4ever's pack - a fork of Gerold Meisinger's opencv-comfyui, ~470 auto-generated cv2.* wrappers plus a few hundred hand-written nodes, all deterministic. When the pack's ancestor was announced on r/comfyui, the framing was honest about where it belongs: small, quick transformations inside a graph, not full OpenCV apps living in Comfy. Homography is squarely in the first camp, and it does a job a diffusion model can't do at all: pixel-exact geometric agreement.
How it works
points_a and points_b come from CV Match Features - the same node's two outputs, in the same order, one point per match. Both are Nx1x2. The node fits H by minimizing reprojection error, but real feature matches always contain outliers, so a robust estimator runs on top: RANSAC picks a minimal sample, counts how many points land within reproj_threshold pixels (default 3) of where H says they should, and keeps the consensus.
The four outputs are the useful part:
- homography - 3×3 float64, mapping
points_aintopoints_b. - inlier_mask -
Nx1uint8, 1 = inlier. Wire it into CV Draw Matches and you can see which matches were lies. - inlier_count - the consensus size.
- found - a boolean you should actually use.
method defaults to RANSAC and that's the right answer for feature matches. The alternatives (LMEDS, RHO, the USAC family, plain least-squares) are refinements for specific cases; least-squares will happily average outliers into a wrong matrix. min_inliers (default 10, minimum 4) is your false-positive gate - RANSAC can converge on noise, and four inliers is the mathematical minimum, not evidence.
Wire homography into cv2_warpPerspective (dsize = the destination image's size, from CV Array Size) or into CV Homography Map if you want it folded into a larger remap chain - that node inverts the matrix by default so you feed it the src→dst H you just got.
The failure contract is the good bit
Raw cv2.findHomography returns None on a degenerate configuration, and graphs die on None. Here, too few points, a non-finite matrix, or an estimate with fewer than min_inliers inliers all return the same safe thing: an identity matrix, a zero mask, inlier_count = 0, found = false. Identity is a no-op warp, so the workflow keeps running and you get the original image instead of a crash.
That means found is your branch. Into a control-flow node - Basic Data Handling's IfElse, or a switch - and bypass the compositing or the drawing when nothing was found. Same principle as the plumbing docs' fallback switches: let the graph decide, don't make the reader remember.
Install
ComfyUI Manager, search ComfyUI CV (repo bmad4ever/comfyui_cv), or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Requires Python ≥ 3.12 and a recent ComfyUI built on the V3 node API - on an old frontend/backend the nodes won't register at all. The pack pins OpenCV 5 (5.0.0.93) because behaviour is curated against it.
Common issues
Mismatched point sets. points_a and points_b must come from the same matcher run. The node raises a clear error when the counts differ, so this one at least tells you.
Plane-only, both ways. A homography is only strictly valid for a planar scene or a pure camera rotation. Two views of a room with depth will fit something with plenty of inliers and warp it wrong - for that you want the fundamental matrix instead.
"found is true but the warp looks wrong." Raise min_inliers (say 15–25) so a weak consensus can't pass, and tighten reproj_threshold toward 2–3 px. Feature matching quality is upstream of everything here; filter weak keypoints first.
Contrib gone missing. If the whole pack imports but contrib-only nodes vanish, another install dropped a non-contrib OpenCV wheel over yours - all the wheels share one site-packages/cv2. tools/repair_opencv_contrib.py --check diagnoses it, --apply fixes it.
Open workflows/exercise_homography_ransac.json first: two images, the match/filter/find chain, an overlay preview and both the matrix and inlier count printed. Then exercise_homography_ar.json for the thing you actually wanted to build.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| points_a | NPARRAY | Source points, Nx1x2 (from 'CV Match Features'). | |
| points_b | NPARRAY | Destination points, Nx1x2. | |
| method | COMBO | RANSAC | Robust estimator. RANSAC is the standard choice for feature matches (which always contain outliers). |
| reproj_threshold | FLOAT | 3.00.1–100 | Maximum reprojection error in pixels for a match to count as an inlier. |
| min_inliers | INT | 104–10000 | Minimum RANSAC inliers for 'found' to be true. Raise it to reject false positives. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| homography | NPARRAY | 3x3 float64 matrix mapping points_a to points_b (identity if not found). |
| inlier_mask | NPARRAY | Nx1 uint8: 1 = inlier. Feed into 'CV Draw Matches'. |
| inlier_count | INT | — |
| found | BOOLEAN | — |