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

CV Filter Points By Distance

Keep the ticks in the band, not the noise

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Filter Points By Distance
  • points
  • points
  • count
◄origin_x0.00►
◄origin_y0.00►
◄min_dist0.00►
◄max_dist1000000.00►

You have a pile of 2D points - corners, keypoint locations, centroids, matched features - and you know where they should be: in a ring around the dial centre, within forty pixels of an anchor, not in the corners of the frame. This node is the geometric version of that knowledge. Keep everything whose distance from an origin falls inside [min_dist, max_dist].

How it works

np.hypot of the offset from (origin_x, origin_y), compared inclusively against both bounds. That's the whole implementation, and it's the right amount of implementation for the job.

The subtlety is the shape of the region, and it's the reason this node exists instead of a bounding-box crop: an annulus is not a rectangle. A corner detector run over a whole image will always find something in the background; restricting to a band around a known feature is how you turn "features in the image" into "features of this object" without a mask.

Inputs and outputs

  • points - an Nx1x2 point array, or None. None or zero points is valid: count = 0, no error.
  • origin_x, origin_y - the reference point in pixels. A dial centre, an anchor, a ray start. Defaults are (0, 0), which means "distance from the top-left corner" if you don't set them - a common accidental filter.
  • min_dist (default 0) - keep points at least this far out. Inclusive.
  • max_dist (default 1000000) - keep points at most this far out. Inclusive. The default is effectively "no upper bound", which is the opposite convention from the point-cloud version of this filter, where max_distance = 0 disables the bound. Worth a second look when you're hopping between the two.

Outputs: points and count. Order is preserved, and there's no mask output - if you need to know which points survived, you'll want a different node, or a labelled point set to begin with.

The workflows it unlocks

  • Dial and gauge reading. CV Enclosing Circle gives you the dial's centre and radius; this node keeps the tick-mark features in a band just inside the rim, and CV Points To Polar then converts them to angles. That's a needle reading without any model.
  • Anchored matching. After a match, points far from any plausible location are almost certainly false matches. An annulus around a known landmark costs nothing and removes a whole class of error before RANSAC sees it.
  • Eyeballing a feature set. Set the band generously, draw the points, look. Seeing what's inside versus outside teaches you what your detector is actually doing better than any statistic will.

The gotcha that costs people an afternoon

min_dist = 0 and max_dist = 1000000 are the defaults, which means if you forget to set the origin, nothing visibly changes and you assume the node is a no-op. It isn't - it's been filtering from the origin in the corner the whole time, and your points were being kept because everything is within a million pixels of anything. Then you set a proper max_dist and the output empties, and it looks like the node broke. It didn't. Set the origin first.

Second: the bounds are in pixels of whatever coordinate space your points are in. If your points came from a resized image, they're in resized coordinates, and a band tuned on the full-size image is now in the wrong units. Check with CV Array Size or the image's dimensions before you tune numbers.

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 built on the V3 node API (older builds register none of this pack). Only dependency: opencv-contrib-python-headless~=5.0.0.93. This node is numpy-only, so the pack's contrib-wheel footgun - installing plain opencv-python over the contrib build silently empties the shared cv2 submodules - doesn't affect it, though it will remove the pack's Contrib-category nodes. tools/repair_opencv_contrib.py --check / --apply exists for that.

The pack is a fork of geroldmeisinger's opencv-comfyui, rewritten by bmad4ever on the modern API, with a loud disclaimer that it's LLM-assisted and not production-ready. A distance threshold is about as auditable as code gets; use it without ceremony.

Categoryimage/CV/points

Inputs (5)

NameTypeDefaultDescription
pointsNPARRAYNx1x2 point array (or None).
origin_xFLOAT0.00-1000000–1000000X pixel coordinate of the reference origin (e.g. the dial center, anchor, or ray start).
origin_yFLOAT0.00-1000000–1000000Y pixel coordinate of the reference origin (e.g. the dial center, anchor, or ray start).
min_distFLOAT0.000–1000000Keep points at least this far (pixels) from the origin.
max_distFLOAT1000000.000–1000000Keep points at most this far (pixels) from the origin.

Outputs (2)

NameTypeDescription
pointsNPARRAY—
countINT—