CV Points To Polar
Turn a bunch of points into bearings around a centre
- points
- radius
- angle
- directions
- count
Sometimes where a point is doesn't matter. How far it is from a centre and which way it lies does. This node rewrites N detections as distance and bearing about an origin you pick.
What it's for
Anything radial. A clock face where you need to know which hand points where. A dial, a gauge, a fan of rays out of a vanishing point, detections arranged in a ring around a target. The moment your problem has a centre, Cartesian pixels are the awkward representation and polar coordinates are the natural one.
The useful trick here is the directions output. It's the unit vector toward each point, and because it's a unit vector, two points on the same ray from the origin collapse to the same feature no matter how far out they sit. If you're clustering points by bearing, that is exactly the feature you want - cluster on directions, keep the original points for drawing, and you group the spokes of a wheel instead of the tips of them. The pack's CV K-Means Points blueprint is built for that shape of problem.
How it works, and the convention trap
Take the origin, subtract it from every point, and you have dx/dy. Radius is the hypot of those. The angle comes from arctan2, and which way it runs is what angle_convention decides:
- clockwise from 12 o'clock (clock) - the default. Twelve o'clock is 0, three o'clock is 90, six o'clock is 180. This reads naturally on an image, where y grows downward, and it is what you want for reading a clock or a dial.
- counter-clockwise from +x (math) - the textbook convention, +x is 0 and the angle grows counter-clockwise assuming y points up.
Angles come out in degrees in [0, 360). Points sitting exactly on the origin get an angle of 0 and a direction of (0,0) rather than a division blow-up, which is a deliberate kindness - a k-means over directions will just see that point as "no bearing".
Inputs and outputs
Inputs are points (an Nx1x2 array; a None input means zero points), origin_x and origin_y in pixel coordinates, and the angle_convention dropdown. The origin is the field you must set: it defaults to (0,0), the top-left corner of the image, which is almost never where your centre is and produces an answer that looks plausible and is wrong.
Four outputs: radius (an N-element distance array), angle (degrees, N elements), directions (Nx1x2 unit vectors), and count. Feed radius and angle back through CV Polar To Points to reconstruct positions, or into the drawing nodes to label a dial. CV Draw Rays takes an origin and points directly if you just want the overlay.
Installing
Pack-wide install. ComfyUI Manager → "ComfyUI CV", or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
You need Python ≥ 3.12, a recent ComfyUI built on the V3 node API, and the pinned opencv-contrib-python-headless~=5.0.0.93. No model files for this one.
Gotchas
Mismatched conventions are the classic failure. If you send angles through some intermediate step and then rebuild points with CV Polar To Points, its angle_convention has to match the one you measured with - otherwise your dial comes out mirrored, which is the sort of bug that looks like everything is working.
Second: this node has no idea what an image's height is. "Counter-clockwise" in the math convention assumes y points up, but in an image y points down, so a math-convention angle drawn onto an image will appear to run the other way unless you flip the y axis at the drawing step. The pack's plotting nodes have an explicit y_axis toggle for precisely this reason. If your angles look inverted, it's not the maths, it's the axis.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| points | NPARRAY | Nx1x2 point array to convert (or None -> 0 points). | |
| origin_x | FLOAT | 0.00-1000000–1000000 | X pixel coordinate of the reference origin (e.g. the dial center, anchor, or ray start). |
| origin_y | FLOAT | 0.00-1000000–1000000 | Y pixel coordinate of the reference origin (e.g. the dial center, anchor, or ray start). |
| angle_convention | COMBO | clockwise from 12 o'clock (clock) | Clock convention: 12h = 0, 3h = 90, 6h = 180. Math convention: +x = 0, growing counter-clockwise (y up). |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| radius | NPARRAY | (N,) float32 distances. |
| angle | NPARRAY | (N,) float32 degrees [0, 360). |
| directions | NPARRAY | Nx1x2 float32 unit vectors (0,0 for points on the origin) - k-means features for grouping by bearing. |
| count | INT | — |