Nodes/ComfyUI CV/CV Filter Points By Mask
ComfyUI Node

CV Filter Points By Mask

Throw out the matches RANSAC didn't believe

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Filter Points By Mask
  • points
  • mask
  • points
  • count

Every robust estimator you use - findHomography, findFundamentalMat, estimateAffine3D - returns two things: a model, and a per-point mask saying which points the model believed. Everybody looks at the model. This node is for the mask, because the mask is how you clean up before triangulation or rectification eats the outliers.

It's a two-input node with one job: keep the points flagged non-zero, drop the rest, preserve the order.

Why the mask is the useful half

Stereo rectification and cv2.triangulatePoints don't have outlier detection. Feed them a matched pair where 30% of the correspondences are wrong and they will dutifully produce a cloud with 30% of its points scattered in space, which then poisons every downstream step - depth maps, point cloud filters, registration. The standard pipeline is: match features → estimate a geometric model with RANSAC → filter both point sets to the inliers → triangulate. This node is step three.

The lockstep part is what makes it work, and the tooltip states the rule exactly: run two of these with the same mask - one on the points from image A, one on the points from image B - and the pair stays aligned, because order is preserved. Points A and B are only correspondences by index; filter one side and you've broken the pairing. Filter both sides with the same mask and you're back to clean correspondences, ready for CV Stereo Rectify (Uncalibrated) or a triangulation call.

Inputs and outputs

  • points - an Nx1x2 point array, or None. Notice this one is required (the 3D version makes its mask optional and its points required; the 2D one does the reverse).
  • mask (optional) - an Nx1 uint8 mask; entries that aren't zero are kept. None means keep everything, so an unwired mask is a pass-through rather than an error. It must have exactly one entry per point: if the lengths don't match, the node raises and tells you the mask has to come from the same matched point set, which is a much better outcome than silently filtering the wrong rows.

Outputs are points and count. That count, next to inlier_count from whatever estimated the model, tells you the whole story in two numbers: how many matches you had, and how many survived the geometry test.

Where it sits

Match → estimate (homography or fundamental matrix) → filter both sides → rectify, triangulate, or measure. Also common on the other side of a detection pipeline: keypoint descriptors that came with a validity flag, or a per-point verdict from any array-level test, used to keep only the samples you trust.

One thing to get right: the mask has to be the one produced for those points. RANSAC masks are index-aligned with the exact arrays you fed it. Re-run the matcher, or reorder the points, and the old mask is garbage - same length, different meaning. Since both this and CV Filter Points 3D By Mask will happily accept a wrong-but-same-length mask, the failure mode is a subtly wrong result rather than a crash. That's the class of bug worth being paranoid about; keep the estimator and the filter wired directly together, not through a detour.

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. The pack's single dependency is opencv-contrib-python-headless~=5.0.0.93. This node does pure numpy indexing, so it survives a broken contrib install - but if you installed plain opencv-python over the contrib wheel, the shared cv2 package loses its contrib submodules and the pack's contrib nodes disappear from the menu. The pack ships tools/repair_opencv_contrib.py --check / --apply for exactly that, since there's no install-time guard.

Gotchas

  • Mask length mismatch → the node raises with a clear message. That's a feature; check that both sets came from the same match.
  • Nothing filtered. A mask of all ones is a valid no-op. Look at inlier_count upstream - if everything is an inlier, your RANSAC threshold is too generous, not this node's fault.
  • Points came back misaligned. You filtered only one side, or applied a mask from a different run. Rebuild the pair.
  • None in, None out. Zero points and a None mask are both valid, count 0.

The pack is bmad4ever's fork of geroldmeisinger's opencv-comfyui, rewritten on the V3 API, and the author's disclaimer - LLM-assisted, not production-grade, no support promised - is worth reading once. A row-selection node is the least scary thing in the pack; the estimator producing the mask is where you should be careful.

Categoryimage/CV/points

Inputs (2)

NameTypeDefaultDescription
pointsNPARRAYNx1x2 point array (or None).
maskoptNPARRAYNx1 uint8 mask: entries != 0 are kept. Must have one entry per point. None = keep all points.

Outputs (2)

NameTypeDescription
pointsNPARRAY—
countINT—