CV Find Fundamental Matrix
Two views, one matrix, and a found flag you must read
- points_a
- points_b
- fundamental
- inlier_mask
- inlier_count
- found
The fundamental matrix is the piece of epipolar geometry that links two views of the same static scene: it tells you, for a point in one image, the line in the other image where its match must lie. That's the thing you need before uncalibrated stereo rectification, before triangulating a point cloud from two photos, and before any epipolar-overlay sanity check. This node estimates it from matched point sets with cv2.findFundamentalMat.
How it works and how it fails
points_a and points_b are Nx1x2 arrays of matched coordinates - the outputs of the pack's feature matching nodes land here directly. From those, the node estimates F and, because you'll be using a robust method, gives you back the inlier mask as well.
The failure contract is the important part. Same as the pack's homography node, and stated explicitly in the description: with fewer than eight points, or a degenerate configuration, you get found = false, a zero matrix and inlier_count = 0 instead of the workflow stopping. You branch on found with a control-flow node. That's the difference between a pipeline that survives its bad frames and one that dies on frame 300 of a batch.
The degeneracy warning is specific and worth repeating because it's the number one way people get a nonsense F: points from a single plane are degenerate for F. A poster, a wall, a book cover, a screenshot - all planar, all give you an unstable matrix that looks fine numerically and produces garbage epipolar lines. If your scene has no depth, you want a homography instead, which is what the planar case is properly described by. The other classic failure is a purely rotational camera move: F encodes the translation between views, and if you didn't translate, there's nothing for it to encode.
Inputs
points_a/points_b- matched points,Nx1x2. Filter them to RANSAC inliers from a previous stage if you have them, but note the estimator here is itself robust, so this is optional.method-FM_RANSAC(the standard choice for feature matches),FM_8POINT(uses all points, only for outlier-free input), orFM_7POINT(needs exactly seven points, and can return multiple solutions).reproj_threshold(default 3, RANSAC only) - maximum distance in pixels from a point to its epipolar line to count as an inlier. Raise it if you're fighting noise, lower it if you're getting epipolar lines that don't line up with anything.confidence(default 0.99, RANSAC/LMedS) - how sure you want the estimate to be.min_inliers(default 15, minimum 8) - the node's own gate onfound. Raise it to reject accidental fits - on a scene with a dominant plane, accidental fits are common and this is your defence.
Outputs
fundamental is the 3×3 float64 matrix, all zeros when not found (the pack's CV Draw Epipolar Lines knows to skip the degenerate lines that would produce). inlier_mask is an Nx1 uint8 mask you feed into CV Draw Matches or the epipolar overlay - and, usefully, into CV Filter Points By Mask, run twice with the same mask to clean point sets A and B in lockstep. inlier_count and found are your control signals.
Where it fits
Matches → F here → inliers → CV Stereo Rectify (Uncalibrated) for a rectified pair, or triangulation for a sparse cloud. The mask-based filtering step in between is what keeps the outliers out of the reconstruction, and skipping it is the difference between a cloud that looks like your scene and one that looks like your scene with fireworks in it.
If you're working with a calibrated rig instead - same camera, known intrinsics - you'd want the essential matrix path, which this pack reaches through its camera-calibration and stereo nodes. F is the uncalibrated route, and uncalibrated means "two photos of the same thing from different places", which is by far the more common case in a ComfyUI graph.
Install
# ComfyUI Manager → search "ComfyUI CV" → install → restart
# or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
cd comfyui_cv && pip install -r requirements.txt
Python ≥ 3.12 and a recent ComfyUI on the V3 node API. Single dependency: opencv-contrib-python-headless~=5.0.0.93. This is a core cv2 function, so it works regardless - but keep the contrib wheel, because installing plain opencv-python over it silently empties the shared cv2 contrib submodules and the pack's Contrib-category nodes disappear (tools/repair_opencv_contrib.py --check / --apply).
When it gives you nothing useful
found: false- under eight points, or degenerate. Check your match count first; this is usually a matcher problem, not a geometry one.found: trueand the epipolar lines are nonsense - you're looking at a plane, or the camera only rotated. Both are degenerate, both look plausible in the numbers.inlier_countis tiny -reproj_thresholdtoo tight, or the matches are bad. Raise the threshold and look at the overlay before you trust anything downstream.- A fit that passes
min_inliersby accident - that's whatmin_inliersis for. Raise it.
bmad4ever's pack is a fork of geroldmeisinger's opencv-comfyui rewritten on the V3 API, and the author's standing disclaimer is that it's LLM-assisted and not production-grade without independent review. The failure contract here - never crash, always answer with found - is the sort of thing that makes a graph survivable, and it's worth copying into whatever you build with it.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| points_a | NPARRAY | Points in image 1, Nx1x2 (from 'CV Match Features'). | |
| points_b | NPARRAY | Corresponding points in image 2, Nx1x2. | |
| method | COMBO | FM_RANSAC | FM_RANSAC is the standard choice for feature matches; FM_8POINT uses all points (only for outlier-free input); FM_7POINT needs exactly 7. |
| reproj_threshold | FLOAT | 3.00.1–100 | Maximum distance in pixels from a point to its epipolar line for it to count as an inlier (RANSAC only). |
| confidence | FLOAT | 0.990.5–1 | Desired probability that the estimate is correct (RANSAC/LMedS only). |
| min_inliers | INT | 158–10000 | Minimum inliers for 'found' to be true. Raise it to reject accidental fits. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| fundamental | NPARRAY | 3x3 float64 fundamental matrix (all zeros if not found - 'CV Draw Epipolar Lines' skips the degenerate lines it produces). |
| inlier_mask | NPARRAY | Nx1 uint8: 1 = inlier. Feed into 'CV Draw Matches' / 'CV Draw Epipolar Lines'. |
| inlier_count | INT | — |
| found | BOOLEAN | — |