CV Filter Points 3D By Mask
Keep only the points a second view agrees with
- points
- mask
- colors
- points
- colors
- count
A point cloud is only as good as the correspondences that built it, and correspondences are where the lies get in. CV Filter Points 3D By Mask is the cleanup step: you have a per-point verdict from somewhere - a RANSAC inlier mask, a validity flag from a sampling node, a neighbourhood-distance test - and you apply it to the cloud, colours and all.
It's the 3D counterpart of CV Filter Points By Mask, which works on 2D points. Same idea, same contract, different dimensionality.
How it works
Keep rows where the mask is non-zero, and slice the parallel colour array with the identical mask so per-vertex colours stay aligned with their points. A None mask passes everything through, so an unwired mask is a no-op rather than an error - which is what lets this node sit in a subgraph where the mask is optional.
Inputs and outputs
points-Nx3orNx1x3, a point cloud. Note this one is required, unlike the 2D version.mask(optional) -Nx1, rows that are non-zero get kept, one entry per point.Nonekeeps all.colors(optional) -Nx3per-vertex colours filtered with the same mask.
Outputs: points, colors, and count so you can see exactly how much survived. That count is worth a debug display - a filter that just removed 90% of your cloud is telling you the previous stage is misbehaving, not that the filter is doing great work.
What to drive it with
The description names the three sources it was built for, and they're a good menu of "what counts as a trustworthy point":
- The inlier mask of
cv2.estimateAffine3D- you fitted a rigid-ish transform between two clouds, and this keeps only the points that supported that fit. The unspoken question is always "why would you fit before filtering?", and the answer is RANSAC: the fit is robust, the mask tells you which points the robust fit liked, and now you use it as a filter. - The
validoutput ofCV Sample Array At Points- points that landed outside the array or on a hole get flagged, so you drop the ones whose values were never real. - The
near_maskofCV Point Cloud Nearest Distance- nearest-neighbour distance below a threshold means "this point has company", which is a coverage/outlier criterion that needs no second view at all.
Chain those and you've assembled a real outlier rejection pipeline: remove the isolated points, keep the ones a fit agreed with, keep the ones a second view confirms.
Where it fits
After reconstruction, before anything that consumes the cloud as geometry - bounding box fit, normals, registration, or export. Note the alignment rule that applies across the whole pack's filter nodes: masks and point arrays must correspond row for row. If your mask came from a different point set than the one you're filtering - same length, different order - this node will happily keep the wrong rows. Downstream, that shows up as a cloud that looks subtly skewed rather than obviously broken, which is the worst kind of bug.
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 - on an older build, none of this pack's nodes appear. Single dependency: opencv-contrib-python-headless~=5.0.0.93; this node is pure numpy, so the pack's contrib-wheel hazard doesn't touch it. Keep contrib anyway: installing plain opencv-python over it silently empties the shared submodules and the Contrib-category nodes disappear (tools/repair_opencv_contrib.py --check / --apply).
Gotchas
- Length mismatch, silent nonsense. Nothing here validates that the mask is the same length as the cloud, unlike the 2D node which raises on a mismatch. If in doubt, check
count. - No
maskmeans no filtering. Easy to forget to wire it and then wonder why the cloud is unchanged. - Colours drifting. You filtered the points in one place and the colours in another. Keep them in the same node.
- It doesn't validate geometry. A mask full of inliers from a bad fit is still a bad cloud. Garbage in, confidently filtered garbage out.
bmad4ever's pack is a fork of geroldmeisinger's opencv-comfyui, rewritten on the modern node API, and the author says openly that it's LLM-assisted and not production-grade without your own review. A mask-and-index filter is the low-risk end of that pack; the risk lives in the estimators that produce the mask.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| points | NPARRAY | Nx3 (or Nx1x3) point cloud. | |
| maskopt | NPARRAY | Nx1 mask: rows != 0 are kept. One entry per point; None keeps all. | |
| colorsopt | NPARRAY | Nx3 per-vertex colours filtered with the same mask. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| points | NPARRAY | — |
| colors | NPARRAY | — |
| count | INT | — |