CV Filter Keypoints
Stop letting tiny details poison your matches
- keypoints
- descriptors
- keypoints
- descriptors
- count
Feature matching fails in a specific, boring way: you give the detector a high-resolution reference photo, it finds four thousand keypoints - most of them on fine texture - and then the same object appears at a third of the size in a cluttered scene where none of that fine texture survives. Those keypoints can't match anything. They just sit in the pool, adding ambiguous candidates and diluting the ratio test. This node removes them.
What it does
It filters by three things, applied in order:
min_size- drops keypoints whose diameter (the scale they were detected at, in pixels of the image they came from) is below the threshold. 0 keeps everything.min_response- drops weak keypoints, i.e. low detector response (corner or contrast strength). 0 keeps everything.top_k- after those two filters, keeps only the K strongest by response. 0 keeps everything.
The node's own advice is to prefer this over guessing a max_features count on the detector: filtering by size and response keeps the structurally meaningful features regardless of how many there are, while a feature-count cap just truncates whatever the detector happened to emit first. That's a good argument, and it's the reason this exists as a separate node rather than three more widgets on CV Detect Features.
Descriptors are filtered in lockstep - the descriptor array is sliced with exactly the same indices, so rows stay aligned with keypoints. Keeping zero keypoints is a valid result: count = 0, and the code deliberately preserves the descriptor array's dtype and column count on the empty slice so you don't get a shape error two nodes later.
The one trap
min_size is an absolute pixel size. It's tempting to filter the reference aggressively, then feed both images through the same node - but if the object appears much smaller in the scene than in the reference, a filter tuned for the reference will delete every usable scene keypoint. The tooltip says it directly: keep this low, or filter only the reference. In practice that means two of these nodes in a graph is normal, with different settings on each side, which is a bit ugly to read but honest about what you're doing.
min_response has the same shape of problem in a subtler form: response scales are detector-specific and image-dependent. A good trick is to leave both at 0, run once, and read the actual responses before setting a threshold - otherwise you're picking a number with no evidence.
Inputs and outputs
keypoints-CV_KEYPOINTS, fromCV Detect Features(or a model-based extractor).descriptors- the paired(N, D)array from the same node. They must come from the same detector run; filter them together or not at all.min_size,min_response,top_k- above.
Outputs: keypoints, descriptors, count. The pair goes straight into the matcher, and the count is worth wiring to a debug display so you can see how much you threw away.
Where it sits in the pipeline
Detect → filter (this node) → match → RANSAC geometry (CV Find Homography (RANSAC) / CV Find Fundamental Matrix). If your inlier count is low and your distance ratios are marginal, this node is usually the highest-leverage thing to add, ahead of tweaking the matcher's ratio threshold - which people reach for first because it's a single number and feels like the tuning knob.
Two situations where it earns its place immediately: matching a reference object against a scene at very different scales, and matching a photograph against a heavily compressed or upscaled version of it, where the small-scale texture differs most.
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 (V3 node API; older builds register none of this pack). Only dependency: opencv-contrib-python-headless~=5.0.0.93. Keep the contrib wheel - installing plain opencv-python over it silently empties the shared contrib submodules and contrib nodes vanish; the pack's tools/repair_opencv_contrib.py (--check, then --apply) is the fix. This node does its own numpy slicing, so it doesn't need contrib at all.
Gotchas
- Zero keypoints.
min_sizein absolute pixels, and your scene image is smaller than you think. Lower it. - Descriptors out of sync. Never mix a keypoint list from one detector run with descriptors from another - the node filters by index, so it will happily produce mismatched pairs if you feed it mismatched inputs.
top_kkeeps the strongest, not the first. It selects the K highest responses and then puts them back into their original relative order, so anything that depends on ordering still makes sense.- Filtering one side only is legitimate. The tooltip endorses it. Your graph will look asymmetric; that's the design, not a mistake.
bmad4ever's pack is a fork of geroldmeisinger's opencv-comfyui, rewritten on the newer node API, and the author states plainly that it's LLM-assisted and not production-grade. This node does a small amount of very predictable array work, which is the low-risk end of that disclosure.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| keypoints | CV_KEYPOINTS | Keypoints to filter (from 'CV Detect Features'). | |
| descriptors | NPARRAY | Descriptors paired with keypoints (same node); kept rows stay aligned with the kept keypoints. | |
| min_size | FLOAT | 00–1000 | Drop keypoints whose diameter (scale, in pixels of the image they were detected in) is below this. 0 = keep all. This is an ABSOLUTE pixel size: if the object appears much smaller in the scene than in the reference, keep this low (or only filter the reference). |
| min_response | FLOAT | 0.0000–1 | Drop weak keypoints whose detector response (corner/contrast strength) is below this. 0 = keep all. |
| top_k | INT | 00–100000 | After the size/response filters, keep only the K strongest by response. 0 = keep all. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| keypoints | CV_KEYPOINTS | — |
| descriptors | NPARRAY | — |
| count | INT | — |