Nodes/ComfyUI CV/CV Match Features
ComfyUI Node

CV Match Features

Pairing two images' keypoints, and the ratio test that makes it work

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Match Features
  • keypoints_a
  • descriptors_a
  • keypoints_b
  • descriptors_b
  • matches
  • points_a
  • points_b
  • count
◄matcherBFMatcher►
◄norm_typeauto►
◄match_moderatio test (Lowe)►
◄ratio0.75►
◄k2►
◄radius0.0►
◄flann_algorithmauto►
◄flann_trees5►
◄flann_checks50►
◄flann_table_number6►
◄flann_key_size12►
◄flann_multi_probe_level1►

Feature matching is the quiet step in a lot of things people care about: panorama stitching, uncalibrated stereo rectification, homography-based alignment, "which of these two shots is the same scene", and the whole visual-odometry chain the pack ships as an example. You detect keypoints in two images, then you need to know which point in A corresponds to which in B. That second half is this node.

It's a wrapper over cv2.BFMatcher and cv2.FlannBasedMatcher with the knobs that actually get used exposed, and it hands you back freshly baked cv2.DMatch objects plus the matched coordinates as plain arrays - because the coordinate arrays are what cv2.findHomography / the pack's CV Find Homography (RANSAC) and cv2.estimateAffine2D actually consume.

How it works

Five required inputs, and the pairing is the part you can get wrong: keypoints_a, descriptors_a, keypoints_b, descriptors_b. Both sides must come from the same detector - descriptors from ORB are binary 32-byte strings and descriptors from SIFT are 128 floats, and a matcher can't compare across that.

Then three decisions:

  • matcher: BFMatcher (brute force, exact, fine up to mid-sized sets) or FLANN (approximate, built for 10k+ descriptors).
  • norm_type: leave it on auto. Auto reads the descriptor dtype and picks HAMMING for uint8 (ORB, BRISK, AKAZE) or L2 for float32 (SIFT). Overriding this is how you get zero matches and no explanation.
  • match_mode: ratio test (Lowe) is the default and the right one. Keep matches whose best distance is clearly better than the runner-up (ratio, default 0.75 - lower is stricter). Cross-check keeps only mutual best matches and is BFMatcher-only. Radius keeps everything within radius and is mostly a debugging mode.

k (default 2) is the kNN count feeding the ratio test; raise it and you consider more candidates for more time. The flann_* group - algorithm, trees, checks, table_number, key_size, multi_probe_level - only matters if you picked FLANN, and you can leave all of it alone until you're chasing speed.

Outputs: matches (the cv2.DMatch list, for CV Draw Matches), points_a and points_b as Nx1x2 float32 in matching order (straight into a homography or affine fit), and count.

Zero matches is a result, not a failure

This is the mindset the node wants from you. Two images of unrelated things produce zero matches, correctly, and nothing raises. Check count, and check your homography node's own found flag downstream rather than assuming a number means a good fit. The classic beginner trap is a hardcoded index or a crop that assumes at least one match exists; wire it through an if/else on count instead.

Two practical gotchas beyond that. Ratio at 0.75 on a scene with lots of repeated texture (windows, tiles) will still produce confident-looking wrong matches - the fix is RANSAC downstream, not a stricter ratio. And points_a/points_b come out Nx1x2 specifically so they drop into cv2's findHomography without reshaping; if you need to do math on them first, CV Reshape Array is in the same pack for exactly that reason.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv

Manager → ComfyUI CV does the same thing. Restart. The declared dependency is opencv-contrib-python-headless~=5.0.0.93, on Python ≥ 3.12 with a V3-API-era ComfyUI. No model files - matching here is pure classical CV, CPU, no downloads. The ONNX-based version lives next door as CV Match Features (Model).

One install gotcha worth repeating because it bites people in every ComfyUI install that has a few CV-adjacent packs: the four OpenCV wheels all share a single site-packages/cv2. Install opencv-python (non-contrib) over the contrib one and contrib functionality silently disappears. The pack has a checker:

python tools/repair_opencv_contrib.py --check

If a custom node is throwing libxcb.so.1: cannot open shared object file on a minimal Linux box (NixOS users report this), that's a system library problem, not a this-pack problem - install the headless wheel or the missing system lib.

Categoryimage/CV/features

Inputs (16)

NameTypeDefaultDescription
keypoints_aCV_KEYPOINTSKeypoints of image A (from 'CV Detect Features').
descriptors_aNPARRAYDescriptors of image A, paired with keypoints_a. Must use the same detector as image B.
keypoints_bCV_KEYPOINTSKeypoints of image B (from 'CV Detect Features').
descriptors_bNPARRAYDescriptors of image B, paired with keypoints_b.
matcherCOMBOBFMatcherBFMatcher: brute-force, exact, fast for small/mid descriptor sets. FLANN: approximate nearest neighbors, faster for large sets (10k+ descriptors).
norm_typeCOMBOautoDistance norm. 'auto' = HAMMING for uint8 (ORB/BRISK/AKAZE), L2 for float32 (SIFT). Override only if you know what you are doing.
match_modeCOMBOratio test (Lowe)Ratio test: keep matches clearly better than the runner-up (robust default). Cross-check: keep only mutual best matches (BFMatcher only). Radius: keep all matches within max distance (all matchers).
ratioFLOAT0.750.1–1Lowe ratio threshold; lower = stricter. Ignored for cross-check and radius modes.
koptINT22–32k for kNN matching. 2 = Lowe's ratio test (standard). Higher k considers more candidates but is slower. Ignored for cross-check mode.
radiusoptFLOAT0.0Max distance for radius matching. 0 = no limit (returns all matches). Only used when mode = radius.
flann_algorithmoptCOMBOautoFLANN index algorithm. 'auto' = KDTREE for float32 descriptors, LSH for binary (uint8). Override to force a specific algorithm.
flann_treesoptINT51–16FLANN KDTREE: number of randomized trees. More = better accuracy, slower build.
flann_checksoptINT501–1000FLANN search: number of leaves to check. Higher = more accurate but slower.
flann_table_numberoptINT61–30FLANN LSH: number of hash tables. Only used for binary descriptors.
flann_key_sizeoptINT124–32FLANN LSH: key bits per table. Only used for binary descriptors.
flann_multi_probe_leveloptINT10–5FLANN LSH: extra search probes per table. 0 = standard LSH, higher = more accurate.

Outputs (4)

NameTypeDescription
matchesCV_MATCHES—
points_aNPARRAYMatched keypoint coordinates in image A, Nx1x2 float32.
points_bNPARRAYMatched keypoint coordinates in image B, Nx1x2 float32.
countINT—