Nodes/ComfyUI CV/CV Match Features (Model)
ComfyUI Node

CV Match Features (Model)

LightGlue matching in ComfyUI, plus the normalization nobody tells you about

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Match Features (Model)
  • keypoints_a
  • descriptors_a
  • keypoints_b
  • descriptors_b
  • space_a
  • space_b
  • matches
  • points_a
  • points_b
  • scores
  • match_count
◄model►
◄engineauto (default engine)►

Classical matching (ORB, SIFT, CV Match Features) is fine until it isn't: low light, big viewpoint change, a texture-poor wall. Learned matchers - LightGlue is the one that matters here - are dramatically better at exactly those cases, and they're a two-stage thing: a learned extractor produces keypoints and descriptors, a learned matcher decides which pairs correspond.

This node is the matcher. Feed it keypoints and descriptors from CV Feature Extract (Model) on both images, pick a LightGlue ONNX, get matches out.

The mechanism, including the bit that bites

Everything runs through cv2.dnn - that's the pack's whole premise, the ONNX models are just another DNN graph to OpenCV. Every .onnx in ComfyUI/models/onnx (subdirectories included) shows up in the model dropdown, so you pick the matcher that matches your extractor. The shipped example is lightglue/disk_lightglue.onnx paired with DISK features.

Here's the part people trip over. LightGlue expects keypoints in a normalized frame centred on the image, roughly −1 to +1, and the exported ONNX doesn't do that conversion for you. This node does it - from the actual height/width on space_a / space_b when you wire the source images in, or, if you leave them unconnected, from the keypoints' own extent, which is the reference implementation's own fallback. Wire the images. The fallback works, but it infers a frame from the points you gave it, and a set of keypoints that doesn't reach the image border will make it guess wrong.

engine picks the DNN backend (leave it on the default unless you have a reason).

Inputs and outputs

Required: keypoints_a, descriptors_a, keypoints_b, descriptors_b, model. Optional: space_a, space_b (accept NPARRAY, IMAGE, MASK or LATENT - only the dims are read) and engine.

Outputs: matches (a cv2.DMatch list sorted best-first, for CV Draw Matches), points_a and points_b as (M,1,2) float32 ready for CV Find Homography (RANSAC), scores (per-match confidence - this is what learned matchers give you that BFMatcher doesn't, and it's a legit thing to threshold on), and match_count.

Zero matches is a valid result with empty outputs, not an error. Same discipline as the classical matcher: gate on match_count.

Install

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

Manager → ComfyUI CV. Then the model, which is not bundled and not in the repo's example inputs:

ComfyUI/models/onnx/
  lightglue/
    disk.onnx              # DISK extractor
    disk_lightglue.onnx    # the matcher this node loads

Both come from the LightGlue-ONNX releases (v0.1.0), Apache-2.0 - one of the few genuinely licence-clean model sources in this whole area, which is worth noticing. The pack's model_sources.txt at the repo root records the URL and licence for every model its workflows touch; read it before you redistribute anything.

Dependencies: opencv-contrib-python-headless~=5.0.0.93, Python ≥ 3.12, V3 node API.

Common issues

"A valid ONNX export that still won't load." The README is unusually honest about this: OpenCV's DNN module supports a subset of what ONNX can express, and the pinned OpenCV version bounds it further. A model that runs perfectly in PyTorch can fail here. If a matcher won't load, that's the likely reason, and there's no fix inside this pack - check the OpenCV version and the model export.

Better matches aren't free time. This runs through cv2.dnn on CPU, one image pair per execution. For a two-image alignment job it's fine. If you're doing it per frame across a video, that's where people discover the pack's own warning and go looking for a GPU path. There isn't one here.

Contrib gotcha, again. All OpenCV wheels share one site-packages/cv2. Installing a plain opencv-python on top of the contrib wheel empties the contrib submodules without any error, and contrib nodes quietly vanish. python tools/repair_opencv_contrib.py --check diagnoses it, --apply fixes it. There's no install-time guard - you have to know.

Categoryimage/CV/dnn

Inputs (8)

NameTypeDefaultDescription
keypoints_aCV_KEYPOINTSKeypoints of image A (from 'CV Feature Extract (Model)').
descriptors_aNPARRAYDescriptors of image A, paired with keypoints_a.
keypoints_bCV_KEYPOINTSKeypoints of image B (from 'CV Feature Extract (Model)').
descriptors_bNPARRAYDescriptors of image B, paired with keypoints_b.
modelCOMBOONNX matcher model from models/onnx (e.g. lightglue/disk_lightglue.onnx).
space_aoptNPARRAY,IMAGE,MASK,LATENTImage A itself - only its height/width are read, to normalize keypoints_a the way LightGlue expects. Leave unconnected to infer the size from the keypoints' own extent instead.
space_boptNPARRAY,IMAGE,MASK,LATENTImage B itself - only its height/width are read, to normalize keypoints_b. Leave unconnected to infer the size from the keypoints' own extent instead.
engineoptCOMBOauto (default engine)DNN engine for inference.

Outputs (5)

NameTypeDescription
matchesCV_MATCHEScv2.DMatch list sorted best-first.
points_aNPARRAY(M, 1, 2) float32 matched point coordinates in A.
points_bNPARRAY(M, 1, 2) float32 matched point coordinates in B.
scoresNPARRAY(M,) float32 match confidence scores.
match_countINTNumber of matches found.